Refactor Value and EvaluatorOutput models in order to get rid of optionals(?)

This commit is contained in:
2020-03-14 13:25:42 +01:00
parent d8744670ed
commit 5b03f55cd4
30 changed files with 60 additions and 78 deletions

View File

@@ -4,7 +4,7 @@ import io.smnp.data.entity.Note
import io.smnp.type.model.Value
import kotlin.reflect.KClass
enum class DataType(val kotlinType: KClass<out Any>?, val stringifier: (Any?) -> String) {
enum class DataType(val kotlinType: KClass<out Any>, val stringifier: (Any) -> String) {
INT(Int::class, { it.toString() }),
FLOAT(Float::class, { it.toString() }),
STRING(String::class, { it.toString() }),
@@ -13,13 +13,9 @@ enum class DataType(val kotlinType: KClass<out Any>?, val stringifier: (Any?) ->
NOTE(Note::class, { it.toString() }),
BOOL(Boolean::class, { it.toString() }),
TYPE(DataType::class, { it.toString() }),
VOID(null, { "void" });
fun isInstance(value: Any?): Boolean {
if(kotlinType == null) {
return value == null
}
VOID(Unit::class, { "void" });
fun isInstance(value: Any): Boolean {
return kotlinType.isInstance(value)
}
@@ -30,17 +26,4 @@ enum class DataType(val kotlinType: KClass<out Any>?, val stringifier: (Any?) ->
override fun toString(): String {
return super.toString().toLowerCase()
}
companion object {
fun inference(value: Any?): DataType {
if (value == null) {
return VOID
}
return values()
.filter { it.kotlinType != null }
.find { it.kotlinType!!.isInstance(value) }
?: throw RuntimeException("Cannot inference type for '$value'")
}
}
}

View File

@@ -48,7 +48,7 @@ class Matcher(val type: DataType?, private val matcher: (Value) -> Boolean, priv
return Matcher(
DataType.MAP,
{
(it.value!! as Map<Value, Value>).entries.all { (k, v) ->
(it.value as Map<Value, Value>).entries.all { (k, v) ->
keyMatchers.any { m -> m.match(k) } && valueMatchers.any { m ->
m.match(
v
@@ -70,7 +70,7 @@ class Matcher(val type: DataType?, private val matcher: (Value) -> Boolean, priv
return matcher.match(value)
}
return (value.value!! as List<Value>).all { match(it) }
return (value.value as List<Value>).all { match(it) }
}
return Matcher(

View File

@@ -4,7 +4,7 @@ import io.smnp.data.entity.Note
import io.smnp.error.ShouldNeverReachThisLineException
import io.smnp.type.enumeration.DataType
data class Value(val type: DataType, val value: Any?, val properties: Map<String, Value> = emptyMap()) {
data class Value(val type: DataType, val value: Any, val properties: Map<String, Value> = emptyMap()) {
init {
if (!type.isInstance(value)) {
throw RuntimeException("'$value' is not of type $type")
@@ -86,7 +86,7 @@ data class Value(val type: DataType, val value: Any?, val properties: Map<String
}
fun void(): Value {
return Value(DataType.VOID, null)
return Value(DataType.VOID, Unit)
}
}
}

View File

@@ -39,6 +39,6 @@ object FunctionEnvironmentProvider {
index: Int
) = (node.identifier as IdentifierNode).token.rawValue to
if (index < actualArgs.size) actualArgs[index]
else evaluator.evaluate(node.defaultValue, environment).value!!
else evaluator.evaluate(node.defaultValue, environment).value
}

View File

@@ -14,7 +14,7 @@ class AccessOperatorEvaluator : Evaluator() {
override fun tryToEvaluate(node: Node, environment: Environment): EvaluatorOutput {
val evaluator = ExpressionEvaluator()
val (lhsNode, _, rhsNode) = (node as AccessOperatorNode)
val lhs = evaluator.evaluate(lhsNode, environment).value!!
val lhs = evaluator.evaluate(lhsNode, environment).value
return when (rhsNode) {
is IdentifierNode -> {
@@ -33,7 +33,7 @@ class AccessOperatorEvaluator : Evaluator() {
val (identifierNode, argsNode) = rhsNode
val identifier = (identifierNode as IdentifierNode).token.rawValue
val arguments =
(argsNode as FunctionCallArgumentsNode).items.map { evaluator.evaluate(it, environment).value!! }
(argsNode as FunctionCallArgumentsNode).items.map { evaluator.evaluate(it, environment).value }
try {
return EvaluatorOutput.value(environment.invokeMethod(lhs, identifier, arguments))
} catch(e: MethodInvocationException) {

View File

@@ -18,7 +18,7 @@ class AssignmentOperatorEvaluator : Evaluator() {
override fun tryToEvaluate(node: Node, environment: Environment): EvaluatorOutput {
val (identifierNode, _, valueNode) = node as AssignmentOperatorNode
val identifier = (identifierNode as IdentifierNode).token.rawValue
val value = evaluator.evaluate(valueNode, environment).value!!
val value = evaluator.evaluate(valueNode, environment).value
if (value.type == DataType.VOID) {
throw PositionException(

View File

@@ -18,7 +18,7 @@ class ConditionEvaluator : Evaluator() {
override fun tryToEvaluate(node: Node, environment: Environment): EvaluatorOutput {
val (conditionNode, trueBranchNode, falseBranchNode) = (node as ConditionNode)
val condition = expressionEvaluator.evaluate(conditionNode, environment).value!!
val condition = expressionEvaluator.evaluate(conditionNode, environment).value
if (condition.type != DataType.BOOL) {
throw PositionException(
@@ -30,7 +30,7 @@ class ConditionEvaluator : Evaluator() {
)
}
if (condition.value!! as Boolean) {
if (condition.value as Boolean) {
return defaultEvaluator.evaluate(trueBranchNode, environment)
} else if (falseBranchNode !is NoneNode) {
return defaultEvaluator.evaluate(falseBranchNode, environment)

View File

@@ -18,7 +18,7 @@ class FunctionCallEvaluator : Evaluator() {
val evaluator = assert(ExpressionEvaluator(), "expression")
val (identifierNode, argsNode) = node as FunctionCallNode
val identifier = (identifierNode as IdentifierNode).token.rawValue
val arguments = (argsNode as FunctionCallArgumentsNode).items.map { evaluator.evaluate(it, environment).value!! }
val arguments = (argsNode as FunctionCallArgumentsNode).items.map { evaluator.evaluate(it, environment).value }
try {
return EvaluatorOutput.value(environment.invokeFunction(identifier, arguments))

View File

@@ -13,7 +13,7 @@ class ListEvaluator : Evaluator() {
val atomEvaluator = ExpressionEvaluator()
val items = (node as ListNode).items
.map { atomEvaluator.evaluate(it, environment) }
.map { it.value!! }
.map { it.value }
return EvaluatorOutput.value(Value.list(items))
}
}

View File

@@ -19,8 +19,8 @@ class LogicOperatorEvaluator : Evaluator() {
override fun tryToEvaluate(node: Node, environment: Environment): EvaluatorOutput {
val evaluator = ExpressionEvaluator()
val (lhsNode, opNode, rhsNode) = (node as LogicOperatorNode)
val lhs = evaluator.evaluate(lhsNode, environment).value!!
val rhs = evaluator.evaluate(rhsNode, environment).value!!
val lhs = evaluator.evaluate(lhsNode, environment).value
val rhs = evaluator.evaluate(rhsNode, environment).value
val operator = (opNode as TokenNode).token.type
if (lhs.type != DataType.BOOL) {

View File

@@ -19,7 +19,7 @@ class LoopEvaluator : Evaluator() {
override fun tryToEvaluate(node: Node, environment: Environment): EvaluatorOutput {
val (iteratorNode, parametersNode, statementNode, filterNode) = node as LoopNode
val iterator = expressionEvaluator.evaluate(iteratorNode, environment).value!!
val iterator = expressionEvaluator.evaluate(iteratorNode, environment).value
environment.pushScope()
val output = when (iterator.type) {
@@ -159,7 +159,7 @@ class LoopEvaluator : Evaluator() {
}
return output { outputs ->
while (expressionEvaluator.evaluate(iteratorNode, environment).value!!.value as Boolean) {
while (expressionEvaluator.evaluate(iteratorNode, environment).value.value as Boolean) {
outputs.add(defaultEvaluator.evaluate(statementNode, environment))
}
}
@@ -177,7 +177,7 @@ class LoopEvaluator : Evaluator() {
private fun filter(filterNode: Node, environment: Environment): Boolean {
if (filterNode != Node.NONE) {
val condition = expressionEvaluator.evaluate(filterNode, environment).value!!
val condition = expressionEvaluator.evaluate(filterNode, environment).value
if (condition.type != BOOL) {
throw PositionException(
EnvironmentException(
@@ -200,7 +200,7 @@ class LoopEvaluator : Evaluator() {
evaluate(outputs)
return when {
outputs.all { it.result == EvaluationResult.VALUE } -> EvaluatorOutput.value(Value.list(outputs.map { it.value!! }))
outputs.all { it.result == EvaluationResult.VALUE } -> EvaluatorOutput.value(Value.list(outputs.map { it.value }))
// Disclaimer: It musn't be ok() because ExpressionEvaluator expects non-ok success output from each
// of subevaluators.

View File

@@ -20,7 +20,7 @@ class MapEvaluator : Evaluator() {
override fun tryToEvaluate(node: Node, environment: Environment): EvaluatorOutput {
val value = (node as MapNode).items
.map { it as MapEntryNode }
.map { getKey(it.key, environment) to evaluator.evaluate(it.value, environment).value!! }
.map { getKey(it.key, environment) to evaluator.evaluate(it.value, environment).value }
.toMap()
return EvaluatorOutput.value(Value.map(value))
@@ -29,7 +29,7 @@ class MapEvaluator : Evaluator() {
private fun getKey(keyNode: Node, environment: Environment): Value {
val key = when (keyNode) {
is IdentifierNode -> Value.string(keyNode.token.rawValue)
else -> evaluator.evaluate(keyNode, environment).value!!
else -> evaluator.evaluate(keyNode, environment).value
}
if (key.type !in listOf(BOOL, INT, NOTE, STRING)) {

View File

@@ -19,7 +19,7 @@ class MinusOperatorEvaluator : Evaluator() {
val operand = evaluator.evaluate(operandNode, environment)
return EvaluatorOutput.value(
when (operand.value!!.type) {
when (operand.value.type) {
DataType.INT -> Value.int(-1 * operand.value.value as Int)
DataType.FLOAT -> Value.float(-1.0f * operand.value.value as Float)
DataType.STRING -> Value.string((operand.value.value as String).reversed())

View File

@@ -16,7 +16,7 @@ class NotOperatorEvaluator : Evaluator() {
override fun tryToEvaluate(node: Node, environment: Environment): EvaluatorOutput {
val (_, operandNode) = (node as NotOperatorNode)
val operand = evaluator.evaluate(operandNode, environment).value!!
val operand = evaluator.evaluate(operandNode, environment).value
if (operand.type != DataType.BOOL) {
throw PositionException(

View File

@@ -16,8 +16,8 @@ class PowerOperatorEvaluator : Evaluator() {
override fun tryToEvaluate(node: Node, environment: Environment): EvaluatorOutput {
val (lhsNode, _, rhsNode) = (node as PowerOperatorNode)
val evaluator = ExpressionEvaluator()
val lhs = evaluator.evaluate(lhsNode, environment).value!!
val rhs = evaluator.evaluate(rhsNode, environment).value!!
val lhs = evaluator.evaluate(lhsNode, environment).value
val rhs = evaluator.evaluate(rhsNode, environment).value
if (!lhs.type.isNumeric() || !rhs.type.isNumeric()) {
throw PositionException(

View File

@@ -19,8 +19,8 @@ class ProductOperatorEvaluator : Evaluator() {
override fun tryToEvaluate(node: Node, environment: Environment): EvaluatorOutput {
val evaluator = ExpressionEvaluator()
val (lhsNode, opNode, rhsNode) = (node as ProductOperatorNode)
val lhs = evaluator.evaluate(lhsNode, environment).value!!
val rhs = evaluator.evaluate(rhsNode, environment).value!!
val lhs = evaluator.evaluate(lhsNode, environment).value
val rhs = evaluator.evaluate(rhsNode, environment).value
val operator = (opNode as TokenNode).token.type
if (!lhs.type.isNumeric() || !rhs.type.isNumeric()) {

View File

@@ -18,8 +18,8 @@ class RelationOperatorEvaluator : Evaluator() {
override fun tryToEvaluate(node: Node, environment: Environment): EvaluatorOutput {
val evaluator = ExpressionEvaluator()
val (lhsNode, opNode, rhsNode) = (node as RelationOperatorNode)
val lhs = evaluator.evaluate(lhsNode, environment).value!!
val rhs = evaluator.evaluate(rhsNode, environment).value!!
val lhs = evaluator.evaluate(lhsNode, environment).value
val rhs = evaluator.evaluate(rhsNode, environment).value
val operator = (opNode as TokenNode).token.rawValue
if (operator in listOf("==", "!=")) {

View File

@@ -5,7 +5,6 @@ import io.smnp.dsl.ast.model.node.ReturnNode
import io.smnp.environment.Environment
import io.smnp.evaluation.model.entity.EvaluatorOutput
import io.smnp.evaluation.model.exception.Return
import io.smnp.type.model.Value
class ReturnEvaluator : Evaluator() {
override fun supportedNodes() = listOf(ReturnNode::class)
@@ -13,7 +12,7 @@ class ReturnEvaluator : Evaluator() {
override fun tryToEvaluate(node: Node, environment: Environment): EvaluatorOutput {
val evaluator = ExpressionEvaluator()
val (valueNode) = node as ReturnNode
val value = evaluator.evaluate(valueNode, environment).value ?: Value.void()
val value = evaluator.evaluate(valueNode, environment).value
// Disclaimer
// Exception system usage to control program execution flow is really bad idea.

View File

@@ -20,8 +20,8 @@ class SumOperatorEvaluator : Evaluator() {
override fun tryToEvaluate(node: Node, environment: Environment): EvaluatorOutput {
val evaluator = ExpressionEvaluator()
val (lhsNode, opNode, rhsNode) = (node as SumOperatorNode)
val lhs = evaluator.evaluate(lhsNode, environment).value!!
val rhs = evaluator.evaluate(rhsNode, environment).value!!
val lhs = evaluator.evaluate(lhsNode, environment).value
val rhs = evaluator.evaluate(rhsNode, environment).value
val operator = (opNode as TokenNode).token.type
return EvaluatorOutput.value(
@@ -37,9 +37,9 @@ class SumOperatorEvaluator : Evaluator() {
return if (areNumeric(lhs, rhs))
unify(lhs, rhs, int = { (l, r) -> Value.int(l + r) }, float = { (l, r) -> Value.float(l + r) })
else if (lhs.type == DataType.STRING)
Value.string(lhs.value!! as String + rhs.value.toString())
Value.string(lhs.value as String + rhs.value.toString())
else if (areLists(lhs, rhs))
Value.list(lhs.value!! as List<Value> + rhs.value!! as List<Value>)
Value.list(lhs.value as List<Value> + rhs.value as List<Value>)
else throw PositionException(
EnvironmentException(
EvaluationException(

View File

@@ -14,6 +14,6 @@ class ThrowEvaluator : Evaluator() {
val valueNode = (node as ThrowNode).value
val value = evaluator.evaluate(valueNode, environment)
throw CustomException(value.value!!.value.toString())
throw CustomException(value.value.value.toString())
}
}

View File

@@ -3,15 +3,15 @@ package io.smnp.evaluation.model.entity
import io.smnp.evaluation.model.enumeration.EvaluationResult
import io.smnp.type.model.Value
class EvaluatorOutput private constructor(val result: EvaluationResult, val value: Value?) {
class EvaluatorOutput private constructor(val result: EvaluationResult, val value: Value) {
override fun toString(): String {
return "$result(${value ?: ""})"
return "$result(${if(result == EvaluationResult.VALUE) value.toString() else ""})"
}
companion object {
fun ok(): EvaluatorOutput {
return EvaluatorOutput(EvaluationResult.OK, null)
return EvaluatorOutput(EvaluationResult.OK, Value.void())
}
fun value(value: Value): EvaluatorOutput {
@@ -19,7 +19,7 @@ class EvaluatorOutput private constructor(val result: EvaluationResult, val valu
}
fun fail(): EvaluatorOutput {
return EvaluatorOutput(EvaluationResult.FAILED, null)
return EvaluatorOutput(EvaluationResult.FAILED, Value.void())
}
}
}

View File

@@ -9,7 +9,7 @@ import io.smnp.type.model.Value
class PrintlnFunction : Function("println") {
override fun define(new: FunctionDefinitionTool) {
new function vararg(allTypes()) body { _, (vararg) ->
println((vararg.value!! as List<Value>).joinToString("") { it.stringify() })
println((vararg.value as List<Value>).joinToString("") { it.stringify() })
Value.void()
}
}

View File

@@ -11,7 +11,7 @@ import io.smnp.type.model.Value
class ReadFunction : Function("read") {
override fun define(new: FunctionDefinitionTool) {
new function simple(optional(ofType(STRING))) body { _, arguments ->
arguments.getOrNull(0)?.let { print(it.value!!) }
arguments.getOrNull(0)?.let { print(it.value) }
Value.string(readLine() ?: "")
}
}

View File

@@ -12,7 +12,7 @@ import io.smnp.type.model.Value
class CharAtMethod : Method(ofType(STRING),"charAt") {
override fun define(new: MethodDefinitionTool) {
new method simple(ofType(INT)) body { _, obj, (index) ->
Value.string((obj.value!! as String).getOrNull(index.value!! as Int)?.toString() ?: throw EvaluationException("Index '${index.value!!}' runs out of string bounds"))
Value.string((obj.value as String).getOrNull(index.value as Int)?.toString() ?: throw EvaluationException("Index '${index.value}' runs out of string bounds"))
}
}
}

View File

@@ -12,8 +12,8 @@ import io.smnp.type.model.Value
class ListAccessMethod : Method(ofType(LIST), "get") {
override fun define(new: MethodDefinitionTool) {
new method simple(ofType(INT)) body { _, value, (index) ->
val list = value.value!! as List<Value>
val i = index.value!! as Int
val list = value.value as List<Value>
val i = index.value as Int
if(i >= list.size) {
throw EvaluationException("Index '$i' runs out of array bounds")

View File

@@ -12,8 +12,8 @@ import io.smnp.type.model.Value
class MapAccessMethod : Method(ofType(MAP), "get") {
override fun define(new: MethodDefinitionTool) {
new method simple(allTypes()) body { _, obj, (key) ->
val map = (obj.value!! as Map<Value, Value>)
map[key] ?: throw EvaluationException("Key '${key.value!!}' not found")
val map = (obj.value as Map<Value, Value>)
map[key] ?: throw EvaluationException("Key '${key.value}' not found")
}
}
}

View File

@@ -22,8 +22,8 @@ class MidiFunction : Function("midi") {
mapOfMatchers(ofType(INT), allTypes())
) body { _, (config, lines) ->
val lines = (lines.value!! as List<Value>).map { it.value!! as List<Value> }
val parameters = configParametersMap(config.value!!)
val lines = (lines.value as List<Value>).map { it.value as List<Value> }
val parameters = configParametersMap(config.value)
MidiSequencer.playLines(lines, parameters)
Value.void()
}
@@ -32,11 +32,11 @@ class MidiFunction : Function("midi") {
mapOfMatchers(allTypes(), allTypes()),
mapOfMatchers(ofType(INT), listOfMatchers(listOf(NOTE, INT, STRING)))
) body { _, (config, channels) ->
val channels = (channels.value!! as Map<Value, Value>).map { (key, value) ->
key.value!! as Int to ((value.value!! as List<Value>).map { it.value!! as List<Value> })
val channels = (channels.value as Map<Value, Value>).map { (key, value) ->
key.value as Int to ((value.value as List<Value>).map { it.value as List<Value> })
}.toMap()
val parameters = configParametersMap(config.value!!)
val parameters = configParametersMap(config.value)
MidiSequencer.playChannels(channels, parameters)
Value.void()
@@ -45,10 +45,10 @@ class MidiFunction : Function("midi") {
private fun configParametersMap(config: Any): Map<String, Any> {
return (config as Map<Value, Value>)
.map { (key, value) -> key.value!! as String to value }
.map { (key, value) -> key.value as String to value }
.map { (key, value) ->
key to when (key) {
"bpm" -> if (value.type == INT) value.value!! else throw EvaluationException("Invalid parameter type: 'bpm' is supposed to be of int type")
"bpm" -> if (value.type == INT) value.value else throw EvaluationException("Invalid parameter type: 'bpm' is supposed to be of int type")
else -> value
}
}

View File

@@ -48,7 +48,7 @@ object MidiSequencer {
NOTE -> {
note(item, channel, noteOnTick, track)
}
INT -> noteOnTick + 4L * PPQ / (item.value!! as Int)
INT -> noteOnTick + 4L * PPQ / (item.value as Int)
STRING -> command(item, channel, noteOnTick, track)
else -> throw ShouldNeverReachThisLineException()
}
@@ -56,7 +56,7 @@ object MidiSequencer {
}
private fun command(item: Value, channel: Int, beginTick: Long, track: Track): Long {
val instruction = item.value!! as String
val instruction = item.value as String
if(instruction.isBlank()) {
throw EvaluationException("Empty strings are not allowed here")
}
@@ -73,7 +73,7 @@ object MidiSequencer {
}
private fun note(item: Value, channel: Int, noteOnTick: Long, track: Track): Long {
val note = item.value!! as Note
val note = item.value as Note
val noteDuration = ((if (note.dot) 1.5 else 1.0) * 4L * PPQ / note.duration).toLong()
val noteOffTick = noteOnTick + noteDuration
track.add(noteOn(note, channel, noteOnTick))

View File

@@ -13,7 +13,7 @@ class ExitFunction : Function("exit") {
override fun define(new: FunctionDefinitionTool) {
new function simple(optional(ofType(INT))) body { _, arguments ->
val exitCode = arguments.getOrNull(0) ?: Value.int(0)
exitProcess(exitCode.value!! as Int)
exitProcess(exitCode.value as Int)
}
}
}

View File

@@ -10,7 +10,7 @@ import io.smnp.type.model.Value
class SleepFunction : Function("sleep") {
override fun define(new: FunctionDefinitionTool) {
new function simple(ofType(INT)) body { _, (milli) ->
Thread.sleep((milli.value!! as Int).toLong())
Thread.sleep((milli.value as Int).toLong())
Value.void()
}
}