123 lines
3.1 KiB
Kotlin
Vendored
123 lines
3.1 KiB
Kotlin
Vendored
// TARGET_BACKEND: JVM
|
|
|
|
// WITH_RUNTIME
|
|
// FULL_JDK
|
|
|
|
package testeval
|
|
|
|
import java.util.LinkedList
|
|
import java.util.Deque
|
|
|
|
interface Expression
|
|
class Num(val value : Int) : Expression
|
|
class Sum(val left : Expression, val right : Expression) : Expression
|
|
class Mult(val left : Expression, val right : Expression) : Expression
|
|
|
|
fun eval(e : Expression) : Int {
|
|
return when (e) {
|
|
is Num -> e.value
|
|
is Sum -> eval(e.left) + eval (e.right)
|
|
is Mult -> eval(e.left) * eval (e.right)
|
|
else -> throw AssertionError("Unknown expression")
|
|
}
|
|
}
|
|
|
|
interface ParseResult<out T> {
|
|
val success : Boolean
|
|
val value : T
|
|
}
|
|
|
|
class Success<T>(override val value : T) : ParseResult<T> {
|
|
public override val success : Boolean = true
|
|
}
|
|
|
|
class Failure(val message : String) : ParseResult<Nothing> {
|
|
override val success = false
|
|
override val value : Nothing = throw UnsupportedOperationException("Don't call value on a Failure")
|
|
}
|
|
|
|
open class Token(val text : String) {
|
|
override fun toString() = text
|
|
}
|
|
object LPAR : Token("(")
|
|
object RPAR : Token(")")
|
|
object PLUS : Token("+")
|
|
object TIMES : Token("*")
|
|
object EOF : Token("EOF")
|
|
class Number(text : String) : Token(text)
|
|
class Error(text : String) : Token("[Error: $text]")
|
|
|
|
|
|
fun tokenize(text : String) : Deque<Token> {
|
|
val result = LinkedList<Token>()
|
|
for (c in text) {
|
|
result.add(when (c) {
|
|
'(' -> LPAR
|
|
')' -> RPAR
|
|
'+' -> PLUS
|
|
'*' -> TIMES
|
|
in '0'..'9' -> Number(c.toString())
|
|
else -> Error(c.toString())
|
|
})
|
|
}
|
|
result.add(EOF)
|
|
return result
|
|
}
|
|
|
|
fun parseSum(tokens : Deque<Token>) : ParseResult<Expression> {
|
|
val left = parseMult(tokens)
|
|
if (!left.success) return left
|
|
|
|
if (tokens.peek() == PLUS) {
|
|
tokens.pop()
|
|
val right = parseSum(tokens)
|
|
if (!right.success) return right
|
|
return Success(Sum(left.value, right.value))
|
|
}
|
|
|
|
return left
|
|
}
|
|
|
|
fun parseMult(tokens : Deque<Token>) : ParseResult<Expression> {
|
|
val left = parseAtomic(tokens)
|
|
if (!left.success) return left
|
|
|
|
if (tokens.peek() == PLUS) {
|
|
tokens.pop()
|
|
val right = parseMult(tokens)
|
|
if (!right.success) return right
|
|
return Success(Mult(left.value, right.value))
|
|
}
|
|
|
|
return left
|
|
}
|
|
|
|
fun parseAtomic(tokens : Deque<Token>) : ParseResult<Expression> {
|
|
val token = tokens.poll()
|
|
return when (token) {
|
|
LPAR -> {
|
|
val result = parseSum(tokens)
|
|
val rpar = tokens.poll()
|
|
if (rpar == RPAR)
|
|
result
|
|
else
|
|
Failure("Expecting ')'")
|
|
}
|
|
is Number -> Success(Num(Integer.parseInt((token as Token).text)))
|
|
else -> Failure("Unexpected EOF")
|
|
}
|
|
}
|
|
|
|
fun parse(text : String) : ParseResult<Expression> = parseSum(tokenize(text))
|
|
|
|
fun box(): String {
|
|
if (1 != eval(Num(1))) return "fail 1"
|
|
if (2 != eval(Sum(Num(1), Num(1)))) return "fail 2"
|
|
if (3 != eval(Mult(Num(3), Num(1)))) return "fail 3"
|
|
if (6 != eval(Mult(Num(3), Sum(Num(1), Num(1))))) return "fail 4"
|
|
|
|
if (1 != eval(parse("1").value)) return "fail 5"
|
|
|
|
return "OK"
|
|
}
|