Hello,
I am trying to write an interpreter and need to detect the runtime class of objects.
I have different nodes in the abstract syntax tree and some are addition of numbers, some are booleans some are strings. The first method is correct in that in returns a Double object. But its return type is cast to Expression.ExpressionLiteral
Does anyone know how I can specify that even though the return type is Any the information from subtypes of Any like Double will not be lost through typecasting to Any?
override fun visitUnaryExpression(obj: Expression.UnaryExpression): Any {
val right = evaluate(obj.right)
println(right.javaClass) // class.Double
println(right is Expression.LiteralExpression) //false
println(right.toString()) //1.0
data class UnaryExpression(val operator:Token,val right:Expression):Expression(){
override fun accept(visitor:Visitor):Any {
// but here the return value is Expression.UnaryExpression
return visitor.visitUnaryExpression(this)
}
}
I’m not sure I fully understood your question, but I think the answer is that you can’t. If the return type of a function is Any
, then at compile time, the only things you can know about it are that it’s an instance of Any
. You can use is
checks to work out what the type is, or do a type cast if you know what it is, but you can’t have a function that returns Any
, and then elsewhere in your code magically treat that return type as a Double
. You need to either check that it’s a Double
, or type cast it to a Double
.
What you might want to look into is using Generics; that might allow you to have the precision you want, but at compile time.
How would you model that with generics? So I have a node
data class BinaryExpression(val left:Expression,val operator:Token,val right:Expression):Expression(){
override fun accept(visitor:Visitor):Any { return visitor.visitBinaryExpression(this) }
and it can hold a double, an int, a string, a boolean. How would you model this with generics?
Keep in mind, I only said that generics might allow you to have the precision you want. You’ve only shown a small amount of your code, so I can’t promise anything.
I’m not sure whether it’s your BinaryExpression
class that holds the value, or the Visitor
? Either way, you could include a type parameter on the class.
Assuming that BinaryExpression
holds the value, and Token
is the value, you could model it like this:
data class BinaryExpression<T>(
val left: Expression,
val operator: Token<T>,
val right: Expression
) : Expression() {
override fun accept(visitor: Visitor): T {
return visitor.visitBinaryExpression(this)
}
}
Or, if Visitor
has the value, like this:
data class BinaryExpression(
val left: Expression,
val operator: Token,
val right: Expression
) : Expression() {
override fun <T> accept(visitor: Visitor<T>): T {
return visitor.visitBinaryExpression(this)
}
}