Introduce basic error handling
This commit is contained in:
@@ -2,7 +2,6 @@ package io.smnp.callable.function
|
|||||||
|
|
||||||
import io.smnp.environment.Environment
|
import io.smnp.environment.Environment
|
||||||
import io.smnp.error.FunctionInvocationException
|
import io.smnp.error.FunctionInvocationException
|
||||||
import io.smnp.error.RuntimeException
|
|
||||||
import io.smnp.type.model.Value
|
import io.smnp.type.model.Value
|
||||||
import io.smnp.type.module.Module
|
import io.smnp.type.module.Module
|
||||||
|
|
||||||
@@ -25,7 +24,7 @@ abstract class Function(val name: String) {
|
|||||||
val (definition, args) = definitions
|
val (definition, args) = definitions
|
||||||
.map { Pair(it, it.signature.parse(arguments.toList())) }
|
.map { Pair(it, it.signature.parse(arguments.toList())) }
|
||||||
.firstOrNull { (_, args) -> args.signatureMatched }
|
.firstOrNull { (_, args) -> args.signatureMatched }
|
||||||
?: throw FunctionInvocationException(this, arguments)
|
?: throw FunctionInvocationException(this, arguments, environment)
|
||||||
|
|
||||||
return definition.body(environment, args.arguments)
|
return definition.body(environment, args.arguments)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package io.smnp.callable.method
|
|||||||
|
|
||||||
import io.smnp.environment.Environment
|
import io.smnp.environment.Environment
|
||||||
import io.smnp.error.MethodInvocationException
|
import io.smnp.error.MethodInvocationException
|
||||||
import io.smnp.error.RuntimeException
|
|
||||||
import io.smnp.type.matcher.Matcher
|
import io.smnp.type.matcher.Matcher
|
||||||
import io.smnp.type.model.Value
|
import io.smnp.type.model.Value
|
||||||
import io.smnp.type.module.Module
|
import io.smnp.type.module.Module
|
||||||
@@ -28,7 +27,7 @@ abstract class Method(val typeMatcher: Matcher, val name: String) {
|
|||||||
val (definition, args) = definitions
|
val (definition, args) = definitions
|
||||||
.map { Pair(it, it.signature.parse(arguments.toList())) }
|
.map { Pair(it, it.signature.parse(arguments.toList())) }
|
||||||
.firstOrNull { (_, args) -> args.signatureMatched }
|
.firstOrNull { (_, args) -> args.signatureMatched }
|
||||||
?: throw MethodInvocationException(this, obj, arguments)
|
?: throw MethodInvocationException(this, obj, arguments, environment)
|
||||||
|
|
||||||
return definition.body(environment, obj, args.arguments)
|
return definition.body(environment, obj, args.arguments)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ interface Environment {
|
|||||||
fun invokeFunction(name: String, arguments: List<Value>): Value
|
fun invokeFunction(name: String, arguments: List<Value>): Value
|
||||||
fun invokeMethod(obj: Value, name: String, arguments: List<Value>): Value
|
fun invokeMethod(obj: Value, name: String, arguments: List<Value>): Value
|
||||||
fun printCallStack()
|
fun printCallStack()
|
||||||
|
fun stackTrace(): String
|
||||||
fun defineFunction(function: Function)
|
fun defineFunction(function: Function)
|
||||||
fun defineMethod(method: Method)
|
fun defineMethod(method: Method)
|
||||||
fun pushScope(scope: MutableMap<String, Value> = mutableMapOf())
|
fun pushScope(scope: MutableMap<String, Value> = mutableMapOf())
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
package io.smnp.error
|
package io.smnp.error
|
||||||
|
|
||||||
class CustomException(message: String?) : Exception(message)
|
class CustomException(message: String?) : SmnpException("Error", message)
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package io.smnp.error
|
||||||
|
|
||||||
|
import io.smnp.environment.Environment
|
||||||
|
|
||||||
|
class EnvironmentException(exception: SmnpException, val environment: Environment) : SmnpException(
|
||||||
|
exception.friendlyName,
|
||||||
|
"${exception.message}\n\nStack trace:\n${environment.stackTrace()}"
|
||||||
|
)
|
||||||
3
api/src/main/kotlin/io/smnp/error/EvaluationException.kt
Normal file
3
api/src/main/kotlin/io/smnp/error/EvaluationException.kt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
package io.smnp.error
|
||||||
|
|
||||||
|
class EvaluationException(message: String?) : SmnpException("Runtime error", message)
|
||||||
@@ -2,8 +2,14 @@ package io.smnp.error
|
|||||||
|
|
||||||
import io.smnp.callable.function.Function
|
import io.smnp.callable.function.Function
|
||||||
import io.smnp.callable.signature.ActualSignatureFormatter.format
|
import io.smnp.callable.signature.ActualSignatureFormatter.format
|
||||||
|
import io.smnp.environment.Environment
|
||||||
import io.smnp.type.model.Value
|
import io.smnp.type.model.Value
|
||||||
|
|
||||||
class FunctionInvocationException(private val function: Function, private val passedArguments: Array<out Value>) : Exception() {
|
class FunctionInvocationException(
|
||||||
override fun toString() = "Invalid signature: ${function.name}${format(passedArguments)}\nAllowed signatures:\n${function.signature}"
|
function: Function,
|
||||||
}
|
passedArguments: Array<out Value>,
|
||||||
|
val environment: Environment
|
||||||
|
) : SmnpException(
|
||||||
|
"Function invocation error",
|
||||||
|
"Invalid signature: ${function.name}${format(passedArguments)}\nAllowed signatures:\n${function.signature}"
|
||||||
|
)
|
||||||
@@ -2,8 +2,15 @@ package io.smnp.error
|
|||||||
|
|
||||||
import io.smnp.callable.method.Method
|
import io.smnp.callable.method.Method
|
||||||
import io.smnp.callable.signature.ActualSignatureFormatter.format
|
import io.smnp.callable.signature.ActualSignatureFormatter.format
|
||||||
|
import io.smnp.environment.Environment
|
||||||
import io.smnp.type.model.Value
|
import io.smnp.type.model.Value
|
||||||
|
|
||||||
class MethodInvocationException(private val method: Method, private val obj: Value, private val passedArguments: Array<out Value>) : Exception() {
|
class MethodInvocationException(
|
||||||
override fun toString() = "Invalid signature: $obj.${method.name}${format(passedArguments)}\nAllowed signatures:\n${method.signature}"
|
method: Method,
|
||||||
}
|
obj: Value,
|
||||||
|
passedArguments: Array<out Value>,
|
||||||
|
val environment: Environment
|
||||||
|
) : SmnpException(
|
||||||
|
"Method invocation error",
|
||||||
|
"Invalid signature: $obj.${method.name}${format(passedArguments)}\nAllowed signatures:\n${method.signature}"
|
||||||
|
)
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
package io.smnp.error
|
|
||||||
|
|
||||||
class RuntimeException(message: String?) : Exception(message)
|
|
||||||
3
api/src/main/kotlin/io/smnp/error/SmnpException.kt
Normal file
3
api/src/main/kotlin/io/smnp/error/SmnpException.kt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
package io.smnp.error
|
||||||
|
|
||||||
|
abstract class SmnpException(val friendlyName: String, message: String? = null) : Exception(message)
|
||||||
@@ -21,4 +21,6 @@ class CallStack {
|
|||||||
fun pretty() {
|
fun pretty() {
|
||||||
items.asReversed().forEachIndexed { index, item -> println("[${items.size - index - 1}] $item") }
|
items.asReversed().forEachIndexed { index, item -> println("[${items.size - index - 1}] $item") }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun stackTrace() = items.asReversed().mapIndexed { index, item -> "[${items.size - index - 1}] $item" }.joinToString("\n")
|
||||||
}
|
}
|
||||||
@@ -2,6 +2,7 @@ package io.smnp.runtime.model
|
|||||||
|
|
||||||
import io.smnp.callable.signature.ActualSignatureFormatter
|
import io.smnp.callable.signature.ActualSignatureFormatter
|
||||||
import io.smnp.collection.Stack
|
import io.smnp.collection.Stack
|
||||||
|
import io.smnp.error.EvaluationException
|
||||||
import io.smnp.type.model.Value
|
import io.smnp.type.model.Value
|
||||||
import io.smnp.type.module.Module
|
import io.smnp.type.module.Module
|
||||||
|
|
||||||
@@ -24,7 +25,7 @@ data class CallStackFrame(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun getVariable(name: String): Value {
|
fun getVariable(name: String): Value {
|
||||||
return scopes.lastOrNull { it.containsKey(name) }?.get(name) ?: throw RuntimeException("Undefined variable `$name`")
|
return scopes.lastOrNull { it.containsKey(name) }?.get(name) ?: throw EvaluationException("Undefined variable `$name`")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun prettyScope() {
|
fun prettyScope() {
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
package io.smnp
|
package io.smnp
|
||||||
|
|
||||||
fun main(args: Array<String>) {
|
import io.smnp.interpreter.DefaultInterpreter
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
fun main(args: Array<String>) {
|
||||||
|
val interpreter = DefaultInterpreter()
|
||||||
|
interpreter.run(File("/home/bartek/Developent/SMNP-Kotlin/examples/scratchpad.mus"))
|
||||||
}
|
}
|
||||||
@@ -2,6 +2,7 @@ package io.smnp.callable.util
|
|||||||
|
|
||||||
import io.smnp.callable.signature.Signature
|
import io.smnp.callable.signature.Signature
|
||||||
import io.smnp.dsl.ast.model.node.*
|
import io.smnp.dsl.ast.model.node.*
|
||||||
|
import io.smnp.error.InvalidSignatureException
|
||||||
import io.smnp.error.ShouldNeverReachThisLineException
|
import io.smnp.error.ShouldNeverReachThisLineException
|
||||||
import io.smnp.type.enumeration.DataType
|
import io.smnp.type.enumeration.DataType
|
||||||
import io.smnp.type.matcher.Matcher
|
import io.smnp.type.matcher.Matcher
|
||||||
@@ -42,7 +43,7 @@ object FunctionSignatureParser {
|
|||||||
}
|
}
|
||||||
}.eachCount().forEach {
|
}.eachCount().forEach {
|
||||||
if (it.value > 1) {
|
if (it.value > 1) {
|
||||||
throw RuntimeException("Duplicated argument name of '${it.key}'")
|
throw InvalidSignatureException("Duplicated argument name of '${it.key}'")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -61,15 +62,15 @@ object FunctionSignatureParser {
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (metadata.hasVararg && metadata.hasOptional) {
|
if (metadata.hasVararg && metadata.hasOptional) {
|
||||||
throw RuntimeException("Optional arguments and vararg cannot be mixed in same signature")
|
throw InvalidSignatureException("Optional arguments and vararg cannot be mixed in same signature")
|
||||||
}
|
}
|
||||||
|
|
||||||
if (metadata.hasRegular && metadata.hasOptional && firstOptional < lastRegular) {
|
if (metadata.hasRegular && metadata.hasOptional && firstOptional < lastRegular) {
|
||||||
throw RuntimeException("Optional arguments should be at the very end of arguments list")
|
throw InvalidSignatureException("Optional arguments should be at the very end of arguments list")
|
||||||
}
|
}
|
||||||
|
|
||||||
if (metadata.hasVararg && vararg != signature.items.size - 1) {
|
if (metadata.hasVararg && vararg != signature.items.size - 1) {
|
||||||
throw RuntimeException("Vararg arguments should be at the very end of arguments list")
|
throw InvalidSignatureException("Vararg arguments should be at the very end of arguments list")
|
||||||
}
|
}
|
||||||
|
|
||||||
return metadata
|
return metadata
|
||||||
@@ -107,7 +108,7 @@ object FunctionSignatureParser {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
throw RuntimeException("Unknown type")
|
throw ShouldNeverReachThisLineException()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun listSpecifier(listSpecifierNode: TypeSpecifierNode): Matcher {
|
private fun listSpecifier(listSpecifierNode: TypeSpecifierNode): Matcher {
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ class ConditionParser : Parser() {
|
|||||||
assert(SubexpressionParser(), "expression"),
|
assert(SubexpressionParser(), "expression"),
|
||||||
terminal(TokenType.CLOSE_PAREN),
|
terminal(TokenType.CLOSE_PAREN),
|
||||||
assert(StatementParser(), "statement")
|
assert(StatementParser(), "statement")
|
||||||
) { (ifToken, condition, trueBranch) ->
|
) { (ifToken, _, condition, _, trueBranch) ->
|
||||||
ConditionNode(ifToken, condition, trueBranch, Node.NONE, Node.NONE)
|
ConditionNode(ifToken, condition, trueBranch, Node.NONE, Node.NONE)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26,7 +26,7 @@ class ConditionParser : Parser() {
|
|||||||
assert(StatementParser(), "statement"),
|
assert(StatementParser(), "statement"),
|
||||||
terminal(TokenType.ELSE),
|
terminal(TokenType.ELSE),
|
||||||
assert(StatementParser(), "statement")
|
assert(StatementParser(), "statement")
|
||||||
) { (ifToken, condition, trueBranch, elseToken, falseBranch) ->
|
) { (ifToken, _, condition, _, trueBranch, elseToken, falseBranch) ->
|
||||||
ConditionNode(ifToken, condition, trueBranch, elseToken, falseBranch)
|
ConditionNode(ifToken, condition, trueBranch, elseToken, falseBranch)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,4 +35,8 @@ class ConditionParser : Parser() {
|
|||||||
ifStatementParser
|
ifStatementParser
|
||||||
).parse(input)
|
).parse(input)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// It is required for destructing list of nodes in ifElseStatementParser object
|
||||||
|
private operator fun <E> List<E>.component6() = this[5];
|
||||||
|
private operator fun <E> List<E>.component7() = this[6];
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ ExtendNode(targetType, identifier, method, extendToken.position)
|
|||||||
assert(loop(terminal(TokenType.OPEN_CURLY), assert(FunctionDefinitionParser(), "method definition or }"), terminal(TokenType.CLOSE_CURLY)) {
|
assert(loop(terminal(TokenType.OPEN_CURLY), assert(FunctionDefinitionParser(), "method definition or }"), terminal(TokenType.CLOSE_CURLY)) {
|
||||||
begin, methods, end -> BlockNode(begin, methods, end)
|
begin, methods, end -> BlockNode(begin, methods, end)
|
||||||
}, "block with methods' definitions or 'with' keyword with single method definition")
|
}, "block with methods' definitions or 'with' keyword with single method definition")
|
||||||
) { (extendToken, targetType, identifier, methods) ->
|
) { (extendToken, targetType, _, identifier, methods) ->
|
||||||
ExtendNode(targetType, identifier, methods, extendToken.position)
|
ExtendNode(targetType, identifier, methods, extendToken.position)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import io.smnp.dsl.token.model.entity.TokenList
|
|||||||
import io.smnp.dsl.token.model.entity.TokenPosition
|
import io.smnp.dsl.token.model.entity.TokenPosition
|
||||||
import io.smnp.dsl.token.model.enumeration.TokenType
|
import io.smnp.dsl.token.model.enumeration.TokenType
|
||||||
import io.smnp.error.InvalidSyntaxException
|
import io.smnp.error.InvalidSyntaxException
|
||||||
|
import io.smnp.error.PositionException
|
||||||
|
|
||||||
abstract class Parser {
|
abstract class Parser {
|
||||||
fun parse(input: TokenList): ParserOutput {
|
fun parse(input: TokenList): ParserOutput {
|
||||||
@@ -178,7 +179,7 @@ abstract class Parser {
|
|||||||
val output = parser.parse(input)
|
val output = parser.parse(input)
|
||||||
|
|
||||||
if (output.result == ParsingResult.FAILED) {
|
if (output.result == ParsingResult.FAILED) {
|
||||||
throw InvalidSyntaxException("Expected $expected, got '${input.current.rawValue}'", input.currentPos())
|
throw PositionException(InvalidSyntaxException("Expected $expected, got '${input.current.rawValue}'"), input.currentPos())
|
||||||
}
|
}
|
||||||
|
|
||||||
return output
|
return output
|
||||||
|
|||||||
@@ -6,10 +6,10 @@ data class TokenPosition(val line: Int, val beginCol: Int, val endCol: Int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "[line ${line+1}, col ${beginCol}]"
|
return "[line ${line+1}, col ${beginCol+1}]"
|
||||||
}
|
}
|
||||||
|
|
||||||
fun short(): String {
|
fun short(): String {
|
||||||
return "${line+1}:${beginCol}"
|
return "${line+1}:${beginCol+1}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -11,6 +11,7 @@ import io.smnp.dsl.token.tokenizer.Tokenizer.Companion.mapValue
|
|||||||
import io.smnp.dsl.token.tokenizer.Tokenizer.Companion.regex
|
import io.smnp.dsl.token.tokenizer.Tokenizer.Companion.regex
|
||||||
import io.smnp.dsl.token.tokenizer.Tokenizer.Companion.separated
|
import io.smnp.dsl.token.tokenizer.Tokenizer.Companion.separated
|
||||||
import io.smnp.error.InvalidSyntaxException
|
import io.smnp.error.InvalidSyntaxException
|
||||||
|
import io.smnp.error.PositionException
|
||||||
|
|
||||||
class DefaultTokenizer : Tokenizer {
|
class DefaultTokenizer : Tokenizer {
|
||||||
private val tokenizers = listOf(
|
private val tokenizers = listOf(
|
||||||
@@ -91,7 +92,7 @@ class DefaultTokenizer : Tokenizer {
|
|||||||
val output = tokenize(line, current, index)
|
val output = tokenize(line, current, index)
|
||||||
|
|
||||||
if (!output.consumed()) {
|
if (!output.consumed()) {
|
||||||
throw InvalidSyntaxException("Unknown symbol ${line[current]}", TokenPosition(index, current, -1))
|
throw PositionException(InvalidSyntaxException("Unknown symbol ${line[current]}"), TokenPosition(index, current, -1))
|
||||||
}
|
}
|
||||||
|
|
||||||
current += output.consumedChars
|
current += output.consumedChars
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package io.smnp.environment
|
|||||||
import io.smnp.callable.function.Function
|
import io.smnp.callable.function.Function
|
||||||
import io.smnp.callable.method.Method
|
import io.smnp.callable.method.Method
|
||||||
import io.smnp.callable.signature.ActualSignatureFormatter.format
|
import io.smnp.callable.signature.ActualSignatureFormatter.format
|
||||||
|
import io.smnp.error.EvaluationException
|
||||||
import io.smnp.ext.DefaultModuleRegistry.requestModuleProviderForPath
|
import io.smnp.ext.DefaultModuleRegistry.requestModuleProviderForPath
|
||||||
import io.smnp.ext.ModuleProvider
|
import io.smnp.ext.ModuleProvider
|
||||||
import io.smnp.interpreter.LanguageModuleInterpreter
|
import io.smnp.interpreter.LanguageModuleInterpreter
|
||||||
@@ -49,11 +50,11 @@ class DefaultEnvironment : Environment {
|
|||||||
override fun invokeFunction(name: String, arguments: List<Value>): Value {
|
override fun invokeFunction(name: String, arguments: List<Value>): Value {
|
||||||
val foundFunctions = rootModule.findFunction(name)
|
val foundFunctions = rootModule.findFunction(name)
|
||||||
if (foundFunctions.isEmpty()) {
|
if (foundFunctions.isEmpty()) {
|
||||||
throw RuntimeException("No function found with name of '$name'")
|
throw EvaluationException("No function found with name of '$name'")
|
||||||
}
|
}
|
||||||
|
|
||||||
if (foundFunctions.size > 1) {
|
if (foundFunctions.size > 1) {
|
||||||
throw RuntimeException("Found ${foundFunctions.size} functions with name of $name: [${foundFunctions.map { it.module.canonicalName }.joinToString()}]")
|
throw EvaluationException("Found ${foundFunctions.size} functions with name of $name: [${foundFunctions.map { it.module.canonicalName }.joinToString()}]")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,11 +70,19 @@ class DefaultEnvironment : Environment {
|
|||||||
override fun invokeMethod(obj: Value, name: String, arguments: List<Value>): Value {
|
override fun invokeMethod(obj: Value, name: String, arguments: List<Value>): Value {
|
||||||
val foundMethods = rootModule.findMethod(obj, name)
|
val foundMethods = rootModule.findMethod(obj, name)
|
||||||
if (foundMethods.isEmpty()) {
|
if (foundMethods.isEmpty()) {
|
||||||
throw RuntimeException("No method found with name of '$name' for ${format(obj)}")
|
throw EvaluationException(
|
||||||
|
"No method found with name of '$name' for ${format(
|
||||||
|
obj
|
||||||
|
)}"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (foundMethods.size > 1) {
|
if (foundMethods.size > 1) {
|
||||||
throw RuntimeException("Found ${foundMethods.size} methods with name of $name for ${format(obj)}: [${foundMethods.map { it.module.canonicalName }.joinToString()}]")
|
throw EvaluationException(
|
||||||
|
"Found ${foundMethods.size} methods with name of $name for ${format(
|
||||||
|
obj
|
||||||
|
)}: [${foundMethods.map { it.module.canonicalName }.joinToString()}]"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val method = foundMethods[0]
|
val method = foundMethods[0]
|
||||||
@@ -89,6 +98,10 @@ class DefaultEnvironment : Environment {
|
|||||||
callStack.pretty()
|
callStack.pretty()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun stackTrace(): String {
|
||||||
|
return callStack.stackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
override fun defineFunction(function: Function) {
|
override fun defineFunction(function: Function) {
|
||||||
rootModule.addFunction(function)
|
rootModule.addFunction(function)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
package io.smnp.error
|
|
||||||
|
|
||||||
import io.smnp.dsl.token.model.entity.TokenPosition
|
|
||||||
|
|
||||||
class EvaluationException(message: String?, val position: TokenPosition?) : Exception(message) {
|
|
||||||
override val message: String?
|
|
||||||
get() = super.message + if(position != null) " $position" else ""
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
package io.smnp.error
|
||||||
|
|
||||||
|
class InvalidSignatureException(message: String?) : SmnpException("Invalid signature", message)
|
||||||
@@ -1,8 +1,3 @@
|
|||||||
package io.smnp.error
|
package io.smnp.error
|
||||||
|
|
||||||
import io.smnp.dsl.token.model.entity.TokenPosition
|
class InvalidSyntaxException(message: String?) : SmnpException("Syntax error", message)
|
||||||
|
|
||||||
class InvalidSyntaxException(message: String?, val position: TokenPosition?) : Exception(message) {
|
|
||||||
override val message: String?
|
|
||||||
get() = super.message + if(position != null) " $position" else ""
|
|
||||||
}
|
|
||||||
3
app/src/main/kotlin/io/smnp/error/ModuleException.kt
Normal file
3
app/src/main/kotlin/io/smnp/error/ModuleException.kt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
package io.smnp.error
|
||||||
|
|
||||||
|
class ModuleException(message: String?) : SmnpException("Module error", message)
|
||||||
8
app/src/main/kotlin/io/smnp/error/PositionException.kt
Normal file
8
app/src/main/kotlin/io/smnp/error/PositionException.kt
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
package io.smnp.error
|
||||||
|
|
||||||
|
import io.smnp.dsl.token.model.entity.TokenPosition
|
||||||
|
|
||||||
|
class PositionException(exception: SmnpException, val position: TokenPosition) : SmnpException(
|
||||||
|
"${exception.friendlyName} $position",
|
||||||
|
exception.message
|
||||||
|
)
|
||||||
@@ -2,31 +2,54 @@ package io.smnp.evaluation.evaluator
|
|||||||
|
|
||||||
import io.smnp.dsl.ast.model.node.*
|
import io.smnp.dsl.ast.model.node.*
|
||||||
import io.smnp.environment.Environment
|
import io.smnp.environment.Environment
|
||||||
|
import io.smnp.error.EnvironmentException
|
||||||
import io.smnp.error.EvaluationException
|
import io.smnp.error.EvaluationException
|
||||||
|
import io.smnp.error.MethodInvocationException
|
||||||
|
import io.smnp.error.PositionException
|
||||||
import io.smnp.evaluation.model.entity.EvaluatorOutput
|
import io.smnp.evaluation.model.entity.EvaluatorOutput
|
||||||
|
|
||||||
class AccessOperatorEvaluator : Evaluator() {
|
class AccessOperatorEvaluator : Evaluator() {
|
||||||
override fun supportedNodes() = listOf(AccessOperatorNode::class)
|
override fun supportedNodes() = listOf(AccessOperatorNode::class)
|
||||||
|
|
||||||
override fun tryToEvaluate(node: Node, environment: Environment): EvaluatorOutput {
|
override fun tryToEvaluate(node: Node, environment: Environment): EvaluatorOutput {
|
||||||
val evaluator = ExpressionEvaluator()
|
val evaluator = ExpressionEvaluator()
|
||||||
val (lhsNode, _, rhsNode) = (node as AccessOperatorNode)
|
val (lhsNode, _, rhsNode) = (node as AccessOperatorNode)
|
||||||
val lhs = evaluator.evaluate(lhsNode, environment).value!!
|
val lhs = evaluator.evaluate(lhsNode, environment).value!!
|
||||||
|
|
||||||
return when (rhsNode) {
|
return when (rhsNode) {
|
||||||
is IdentifierNode -> {
|
is IdentifierNode -> {
|
||||||
val rhs = rhsNode.token.rawValue
|
val rhs = rhsNode.token.rawValue
|
||||||
EvaluatorOutput.value(lhs.properties[rhs] ?: throw EvaluationException("Unknown property $rhs of type ${lhs.type.name.toLowerCase()}", rhsNode.position))
|
EvaluatorOutput.value(
|
||||||
|
lhs.properties[rhs] ?: throw PositionException(
|
||||||
|
EnvironmentException(
|
||||||
|
EvaluationException("Unknown property $rhs of type ${lhs.type.name.toLowerCase()}"),
|
||||||
|
environment
|
||||||
|
),
|
||||||
|
rhsNode.position
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
is FunctionCallNode -> {
|
||||||
|
val (identifierNode, argsNode) = rhsNode
|
||||||
|
val identifier = (identifierNode as IdentifierNode).token.rawValue
|
||||||
|
val arguments =
|
||||||
|
(argsNode as FunctionCallArgumentsNode).items.map { evaluator.evaluate(it, environment).value!! }
|
||||||
|
try {
|
||||||
|
return EvaluatorOutput.value(environment.invokeMethod(lhs, identifier, arguments))
|
||||||
|
} catch(e: MethodInvocationException) {
|
||||||
|
throw PositionException(EnvironmentException(e, environment), identifierNode.position)
|
||||||
|
} catch(e: EvaluationException) {
|
||||||
|
throw PositionException(EnvironmentException(e, environment), identifierNode.position)
|
||||||
}
|
}
|
||||||
is FunctionCallNode -> {
|
}
|
||||||
val (identifierNode, argsNode) = rhsNode
|
else -> {
|
||||||
val identifier = (identifierNode as IdentifierNode).token.rawValue
|
throw PositionException(
|
||||||
val arguments = (argsNode as FunctionCallArgumentsNode).items.map { evaluator.evaluate(it, environment).value!! }
|
EnvironmentException(
|
||||||
return EvaluatorOutput.value(environment.invokeMethod(lhs, identifier, arguments))
|
EvaluationException("Invalid property access type - only property name and method call are allowed"),
|
||||||
}
|
environment
|
||||||
else -> {
|
), rhsNode.position
|
||||||
throw EvaluationException("Invalid property access type - only property name and method call are allowed", rhsNode.position)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,7 +4,9 @@ import io.smnp.dsl.ast.model.node.AssignmentOperatorNode
|
|||||||
import io.smnp.dsl.ast.model.node.IdentifierNode
|
import io.smnp.dsl.ast.model.node.IdentifierNode
|
||||||
import io.smnp.dsl.ast.model.node.Node
|
import io.smnp.dsl.ast.model.node.Node
|
||||||
import io.smnp.environment.Environment
|
import io.smnp.environment.Environment
|
||||||
|
import io.smnp.error.EnvironmentException
|
||||||
import io.smnp.error.EvaluationException
|
import io.smnp.error.EvaluationException
|
||||||
|
import io.smnp.error.PositionException
|
||||||
import io.smnp.evaluation.model.entity.EvaluatorOutput
|
import io.smnp.evaluation.model.entity.EvaluatorOutput
|
||||||
import io.smnp.type.enumeration.DataType
|
import io.smnp.type.enumeration.DataType
|
||||||
|
|
||||||
@@ -19,8 +21,11 @@ class AssignmentOperatorEvaluator : Evaluator() {
|
|||||||
val value = evaluator.evaluate(valueNode, environment).value!!
|
val value = evaluator.evaluate(valueNode, environment).value!!
|
||||||
|
|
||||||
if (value.type == DataType.VOID) {
|
if (value.type == DataType.VOID) {
|
||||||
throw EvaluationException(
|
throw PositionException(
|
||||||
"Right hand side expression of assignment operation has returned nothing",
|
EnvironmentException(
|
||||||
|
EvaluationException("Right hand side expression of assignment operation has returned nothing"),
|
||||||
|
environment
|
||||||
|
),
|
||||||
valueNode.position
|
valueNode.position
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,29 +4,38 @@ import io.smnp.dsl.ast.model.node.ConditionNode
|
|||||||
import io.smnp.dsl.ast.model.node.Node
|
import io.smnp.dsl.ast.model.node.Node
|
||||||
import io.smnp.dsl.ast.model.node.NoneNode
|
import io.smnp.dsl.ast.model.node.NoneNode
|
||||||
import io.smnp.environment.Environment
|
import io.smnp.environment.Environment
|
||||||
|
import io.smnp.error.EnvironmentException
|
||||||
import io.smnp.error.EvaluationException
|
import io.smnp.error.EvaluationException
|
||||||
|
import io.smnp.error.PositionException
|
||||||
import io.smnp.evaluation.model.entity.EvaluatorOutput
|
import io.smnp.evaluation.model.entity.EvaluatorOutput
|
||||||
import io.smnp.type.enumeration.DataType
|
import io.smnp.type.enumeration.DataType
|
||||||
|
|
||||||
class ConditionEvaluator : Evaluator() {
|
class ConditionEvaluator : Evaluator() {
|
||||||
override fun supportedNodes() = listOf(ConditionNode::class)
|
private val expressionEvaluator = ExpressionEvaluator()
|
||||||
|
private val defaultEvaluator = DefaultEvaluator()
|
||||||
|
|
||||||
override fun tryToEvaluate(node: Node, environment: Environment): EvaluatorOutput {
|
override fun supportedNodes() = listOf(ConditionNode::class)
|
||||||
val expressionEvaluator = ExpressionEvaluator()
|
|
||||||
val defaultEvaluator = DefaultEvaluator()
|
|
||||||
val (conditionNode, trueBranchNode, falseBranchNode) = (node as ConditionNode)
|
|
||||||
val condition = expressionEvaluator.evaluate(conditionNode, environment).value!!
|
|
||||||
|
|
||||||
if(condition.type != DataType.BOOL) {
|
override fun tryToEvaluate(node: Node, environment: Environment): EvaluatorOutput {
|
||||||
throw EvaluationException("Condition should be of bool type, found '${condition.value}'", conditionNode.position)
|
val (conditionNode, trueBranchNode, falseBranchNode) = (node as ConditionNode)
|
||||||
}
|
val condition = expressionEvaluator.evaluate(conditionNode, environment).value!!
|
||||||
|
|
||||||
if(condition.value!! as Boolean) {
|
if (condition.type != DataType.BOOL) {
|
||||||
return defaultEvaluator.evaluate(trueBranchNode, environment)
|
throw PositionException(
|
||||||
} else if(falseBranchNode !is NoneNode) {
|
EnvironmentException(
|
||||||
return defaultEvaluator.evaluate(falseBranchNode, environment)
|
EvaluationException("Condition should be of bool type, found '${condition.value}'"),
|
||||||
}
|
environment
|
||||||
|
),
|
||||||
|
conditionNode.position
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return EvaluatorOutput.ok()
|
if (condition.value!! as Boolean) {
|
||||||
}
|
return defaultEvaluator.evaluate(trueBranchNode, environment)
|
||||||
|
} else if (falseBranchNode !is NoneNode) {
|
||||||
|
return defaultEvaluator.evaluate(falseBranchNode, environment)
|
||||||
|
}
|
||||||
|
|
||||||
|
return EvaluatorOutput.ok()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,9 @@ package io.smnp.evaluation.evaluator
|
|||||||
|
|
||||||
import io.smnp.dsl.ast.model.node.Node
|
import io.smnp.dsl.ast.model.node.Node
|
||||||
import io.smnp.environment.Environment
|
import io.smnp.environment.Environment
|
||||||
|
import io.smnp.error.EnvironmentException
|
||||||
import io.smnp.error.EvaluationException
|
import io.smnp.error.EvaluationException
|
||||||
|
import io.smnp.error.PositionException
|
||||||
import io.smnp.evaluation.model.entity.EvaluatorOutput
|
import io.smnp.evaluation.model.entity.EvaluatorOutput
|
||||||
import io.smnp.evaluation.model.enumeration.EvaluationResult
|
import io.smnp.evaluation.model.enumeration.EvaluationResult
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
@@ -62,7 +64,10 @@ abstract class Evaluator {
|
|||||||
val output = evaluator.evaluate(node, environment)
|
val output = evaluator.evaluate(node, environment)
|
||||||
|
|
||||||
if (output.result == EvaluationResult.FAILED) {
|
if (output.result == EvaluationResult.FAILED) {
|
||||||
throw EvaluationException("Expected $expected", node.position)
|
throw PositionException(
|
||||||
|
EnvironmentException(EvaluationException("Expected $expected"), environment),
|
||||||
|
node.position
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return output
|
return output
|
||||||
|
|||||||
@@ -4,20 +4,29 @@ import io.smnp.callable.method.CustomMethod
|
|||||||
import io.smnp.callable.util.FunctionSignatureParser
|
import io.smnp.callable.util.FunctionSignatureParser
|
||||||
import io.smnp.dsl.ast.model.node.*
|
import io.smnp.dsl.ast.model.node.*
|
||||||
import io.smnp.environment.Environment
|
import io.smnp.environment.Environment
|
||||||
|
import io.smnp.error.EnvironmentException
|
||||||
|
import io.smnp.error.PositionException
|
||||||
|
import io.smnp.error.SmnpException
|
||||||
import io.smnp.evaluation.model.entity.EvaluatorOutput
|
import io.smnp.evaluation.model.entity.EvaluatorOutput
|
||||||
|
|
||||||
class ExtendEvaluator : Evaluator() {
|
class ExtendEvaluator : Evaluator() {
|
||||||
override fun supportedNodes() = listOf(ExtendNode::class)
|
override fun supportedNodes() = listOf(ExtendNode::class)
|
||||||
|
|
||||||
override fun tryToEvaluate(node: Node, environment: Environment): EvaluatorOutput {
|
override fun tryToEvaluate(node: Node, environment: Environment): EvaluatorOutput {
|
||||||
val (typeNode, identifierNode, methodsNode) = node as ExtendNode
|
val (typeNode, identifierNode, methodsNode) = node as ExtendNode
|
||||||
val type = FunctionSignatureParser.matcherForSingleTypeNode(typeNode as SingleTypeNode)
|
val type = FunctionSignatureParser.matcherForSingleTypeNode(typeNode as SingleTypeNode)
|
||||||
val identifier = (identifierNode as IdentifierNode).token.rawValue
|
val identifier = (identifierNode as IdentifierNode).token.rawValue
|
||||||
|
|
||||||
methodsNode.children
|
methodsNode.children
|
||||||
.map { CustomMethod.create(type, identifier, it as FunctionDefinitionNode) }
|
.map { it to CustomMethod.create(type, identifier, it as FunctionDefinitionNode) }
|
||||||
.forEach { environment.defineMethod(it) }
|
.forEach {
|
||||||
|
try {
|
||||||
|
environment.defineMethod(it.second)
|
||||||
|
} catch (e: SmnpException) {
|
||||||
|
throw PositionException(EnvironmentException(e, environment), it.first.position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return EvaluatorOutput.ok()
|
return EvaluatorOutput.ok()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,6 +5,10 @@ import io.smnp.dsl.ast.model.node.FunctionCallNode
|
|||||||
import io.smnp.dsl.ast.model.node.IdentifierNode
|
import io.smnp.dsl.ast.model.node.IdentifierNode
|
||||||
import io.smnp.dsl.ast.model.node.Node
|
import io.smnp.dsl.ast.model.node.Node
|
||||||
import io.smnp.environment.Environment
|
import io.smnp.environment.Environment
|
||||||
|
import io.smnp.error.EnvironmentException
|
||||||
|
import io.smnp.error.EvaluationException
|
||||||
|
import io.smnp.error.FunctionInvocationException
|
||||||
|
import io.smnp.error.PositionException
|
||||||
import io.smnp.evaluation.model.entity.EvaluatorOutput
|
import io.smnp.evaluation.model.entity.EvaluatorOutput
|
||||||
|
|
||||||
class FunctionCallEvaluator : Evaluator() {
|
class FunctionCallEvaluator : Evaluator() {
|
||||||
@@ -16,6 +20,12 @@ class FunctionCallEvaluator : Evaluator() {
|
|||||||
val identifier = (identifierNode as IdentifierNode).token.rawValue
|
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!! }
|
||||||
|
|
||||||
return EvaluatorOutput.value(environment.invokeFunction(identifier, arguments))
|
try {
|
||||||
|
return EvaluatorOutput.value(environment.invokeFunction(identifier, arguments))
|
||||||
|
} catch(e: FunctionInvocationException) {
|
||||||
|
throw PositionException(EnvironmentException(e, environment), node.position)
|
||||||
|
} catch(e: EvaluationException) {
|
||||||
|
throw PositionException(EnvironmentException(e, environment), node.position)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,6 +4,9 @@ import io.smnp.callable.function.CustomFunction
|
|||||||
import io.smnp.dsl.ast.model.node.FunctionDefinitionNode
|
import io.smnp.dsl.ast.model.node.FunctionDefinitionNode
|
||||||
import io.smnp.dsl.ast.model.node.Node
|
import io.smnp.dsl.ast.model.node.Node
|
||||||
import io.smnp.environment.Environment
|
import io.smnp.environment.Environment
|
||||||
|
import io.smnp.error.EnvironmentException
|
||||||
|
import io.smnp.error.PositionException
|
||||||
|
import io.smnp.error.SmnpException
|
||||||
import io.smnp.evaluation.model.entity.EvaluatorOutput
|
import io.smnp.evaluation.model.entity.EvaluatorOutput
|
||||||
|
|
||||||
class FunctionDefinitionEvaluator : Evaluator() {
|
class FunctionDefinitionEvaluator : Evaluator() {
|
||||||
@@ -11,7 +14,13 @@ class FunctionDefinitionEvaluator : Evaluator() {
|
|||||||
|
|
||||||
override fun tryToEvaluate(node: Node, environment: Environment): EvaluatorOutput {
|
override fun tryToEvaluate(node: Node, environment: Environment): EvaluatorOutput {
|
||||||
val function = CustomFunction.create(node as FunctionDefinitionNode)
|
val function = CustomFunction.create(node as FunctionDefinitionNode)
|
||||||
environment.defineFunction(function)
|
|
||||||
|
try {
|
||||||
|
environment.defineFunction(function)
|
||||||
|
} catch(e: SmnpException) {
|
||||||
|
throw PositionException(EnvironmentException(e, environment), node.position)
|
||||||
|
}
|
||||||
|
|
||||||
return EvaluatorOutput.ok()
|
return EvaluatorOutput.ok()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,6 +3,9 @@ package io.smnp.evaluation.evaluator
|
|||||||
import io.smnp.dsl.ast.model.node.IdentifierNode
|
import io.smnp.dsl.ast.model.node.IdentifierNode
|
||||||
import io.smnp.dsl.ast.model.node.Node
|
import io.smnp.dsl.ast.model.node.Node
|
||||||
import io.smnp.environment.Environment
|
import io.smnp.environment.Environment
|
||||||
|
import io.smnp.error.EnvironmentException
|
||||||
|
import io.smnp.error.EvaluationException
|
||||||
|
import io.smnp.error.PositionException
|
||||||
import io.smnp.evaluation.model.entity.EvaluatorOutput
|
import io.smnp.evaluation.model.entity.EvaluatorOutput
|
||||||
|
|
||||||
class IdentifierEvaluator : Evaluator() {
|
class IdentifierEvaluator : Evaluator() {
|
||||||
@@ -10,6 +13,11 @@ class IdentifierEvaluator : Evaluator() {
|
|||||||
|
|
||||||
override fun tryToEvaluate(node: Node, environment: Environment): EvaluatorOutput {
|
override fun tryToEvaluate(node: Node, environment: Environment): EvaluatorOutput {
|
||||||
val identifier = (node as IdentifierNode).token.rawValue
|
val identifier = (node as IdentifierNode).token.rawValue
|
||||||
return EvaluatorOutput.value(environment.getVariable(identifier))
|
|
||||||
|
try {
|
||||||
|
return EvaluatorOutput.value(environment.getVariable(identifier))
|
||||||
|
} catch (e: EvaluationException) {
|
||||||
|
throw PositionException(EnvironmentException(e, environment), node.position)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,6 +4,9 @@ import io.smnp.dsl.ast.model.node.IdentifierNode
|
|||||||
import io.smnp.dsl.ast.model.node.ImportNode
|
import io.smnp.dsl.ast.model.node.ImportNode
|
||||||
import io.smnp.dsl.ast.model.node.Node
|
import io.smnp.dsl.ast.model.node.Node
|
||||||
import io.smnp.environment.Environment
|
import io.smnp.environment.Environment
|
||||||
|
import io.smnp.error.EnvironmentException
|
||||||
|
import io.smnp.error.PositionException
|
||||||
|
import io.smnp.error.SmnpException
|
||||||
import io.smnp.evaluation.model.entity.EvaluatorOutput
|
import io.smnp.evaluation.model.entity.EvaluatorOutput
|
||||||
|
|
||||||
class ImportEvaluator : Evaluator() {
|
class ImportEvaluator : Evaluator() {
|
||||||
@@ -11,7 +14,12 @@ class ImportEvaluator : Evaluator() {
|
|||||||
|
|
||||||
override fun tryToEvaluate(node: Node, environment: Environment): EvaluatorOutput {
|
override fun tryToEvaluate(node: Node, environment: Environment): EvaluatorOutput {
|
||||||
val path = (node as ImportNode).path.joinToString(".") { (it as IdentifierNode).token.rawValue }
|
val path = (node as ImportNode).path.joinToString(".") { (it as IdentifierNode).token.rawValue }
|
||||||
environment.loadModule(path)
|
|
||||||
|
try {
|
||||||
|
environment.loadModule(path)
|
||||||
|
} catch(e: SmnpException) {
|
||||||
|
throw PositionException(EnvironmentException(e, environment), node.position)
|
||||||
|
}
|
||||||
|
|
||||||
return EvaluatorOutput.ok()
|
return EvaluatorOutput.ok()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,34 +5,50 @@ import io.smnp.dsl.ast.model.node.Node
|
|||||||
import io.smnp.dsl.ast.model.node.TokenNode
|
import io.smnp.dsl.ast.model.node.TokenNode
|
||||||
import io.smnp.dsl.token.model.enumeration.TokenType
|
import io.smnp.dsl.token.model.enumeration.TokenType
|
||||||
import io.smnp.environment.Environment
|
import io.smnp.environment.Environment
|
||||||
|
import io.smnp.error.EnvironmentException
|
||||||
import io.smnp.error.EvaluationException
|
import io.smnp.error.EvaluationException
|
||||||
|
import io.smnp.error.PositionException
|
||||||
import io.smnp.error.ShouldNeverReachThisLineException
|
import io.smnp.error.ShouldNeverReachThisLineException
|
||||||
import io.smnp.evaluation.model.entity.EvaluatorOutput
|
import io.smnp.evaluation.model.entity.EvaluatorOutput
|
||||||
import io.smnp.type.enumeration.DataType
|
import io.smnp.type.enumeration.DataType
|
||||||
import io.smnp.type.model.Value
|
import io.smnp.type.model.Value
|
||||||
|
|
||||||
class LogicOperatorEvaluator : Evaluator() {
|
class LogicOperatorEvaluator : Evaluator() {
|
||||||
override fun supportedNodes() = listOf(LogicOperatorNode::class)
|
override fun supportedNodes() = listOf(LogicOperatorNode::class)
|
||||||
|
|
||||||
override fun tryToEvaluate(node: Node, environment: Environment): EvaluatorOutput {
|
override fun tryToEvaluate(node: Node, environment: Environment): EvaluatorOutput {
|
||||||
val evaluator = ExpressionEvaluator()
|
val evaluator = ExpressionEvaluator()
|
||||||
val (lhsNode, opNode, rhsNode) = (node as LogicOperatorNode)
|
val (lhsNode, opNode, rhsNode) = (node as LogicOperatorNode)
|
||||||
val lhs = evaluator.evaluate(lhsNode, environment).value!!
|
val lhs = evaluator.evaluate(lhsNode, environment).value!!
|
||||||
val rhs = evaluator.evaluate(rhsNode, environment).value!!
|
val rhs = evaluator.evaluate(rhsNode, environment).value!!
|
||||||
val operator = (opNode as TokenNode).token.type
|
val operator = (opNode as TokenNode).token.type
|
||||||
|
|
||||||
if(lhs.type != DataType.BOOL) {
|
if (lhs.type != DataType.BOOL) {
|
||||||
throw EvaluationException("Operator '${operator.token}' supports only bool types", lhsNode.position)
|
throw PositionException(
|
||||||
}
|
EnvironmentException(
|
||||||
|
EvaluationException("Operator '${operator.token}' supports only bool types"),
|
||||||
|
environment
|
||||||
|
),
|
||||||
|
lhsNode.position
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
if(rhs.type != DataType.BOOL) {
|
if (rhs.type != DataType.BOOL) {
|
||||||
throw EvaluationException("Operator '${operator.token}' supports only bool types", rhsNode.position)
|
throw PositionException(
|
||||||
}
|
EnvironmentException(
|
||||||
|
EvaluationException("Operator '${operator.token}' supports only bool types"),
|
||||||
|
environment
|
||||||
|
),
|
||||||
|
rhsNode.position
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return EvaluatorOutput.value(when(operator) {
|
return EvaluatorOutput.value(
|
||||||
|
when (operator) {
|
||||||
TokenType.AND -> Value.bool((lhs.value as Boolean) && (rhs.value as Boolean))
|
TokenType.AND -> Value.bool((lhs.value as Boolean) && (rhs.value as Boolean))
|
||||||
TokenType.OR -> Value.bool((lhs.value as Boolean) || (rhs.value as Boolean))
|
TokenType.OR -> Value.bool((lhs.value as Boolean) || (rhs.value as Boolean))
|
||||||
else -> throw ShouldNeverReachThisLineException()
|
else -> throw ShouldNeverReachThisLineException()
|
||||||
})
|
}
|
||||||
}
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -4,7 +4,9 @@ import io.smnp.dsl.ast.model.node.IdentifierNode
|
|||||||
import io.smnp.dsl.ast.model.node.LoopNode
|
import io.smnp.dsl.ast.model.node.LoopNode
|
||||||
import io.smnp.dsl.ast.model.node.Node
|
import io.smnp.dsl.ast.model.node.Node
|
||||||
import io.smnp.environment.Environment
|
import io.smnp.environment.Environment
|
||||||
|
import io.smnp.error.EnvironmentException
|
||||||
import io.smnp.error.EvaluationException
|
import io.smnp.error.EvaluationException
|
||||||
|
import io.smnp.error.PositionException
|
||||||
import io.smnp.evaluation.model.entity.EvaluatorOutput
|
import io.smnp.evaluation.model.entity.EvaluatorOutput
|
||||||
import io.smnp.evaluation.model.enumeration.EvaluationResult
|
import io.smnp.evaluation.model.enumeration.EvaluationResult
|
||||||
import io.smnp.type.enumeration.DataType.*
|
import io.smnp.type.enumeration.DataType.*
|
||||||
@@ -26,9 +28,13 @@ class LoopEvaluator : Evaluator() {
|
|||||||
LIST -> evaluateForList(iterator, parametersNode, statementNode, filterNode, environment)
|
LIST -> evaluateForList(iterator, parametersNode, statementNode, filterNode, environment)
|
||||||
MAP -> evaluateForMap(iterator, parametersNode, statementNode, filterNode, environment)
|
MAP -> evaluateForMap(iterator, parametersNode, statementNode, filterNode, environment)
|
||||||
BOOL -> evaluateForBool(iteratorNode, parametersNode, statementNode, filterNode, environment)
|
BOOL -> evaluateForBool(iteratorNode, parametersNode, statementNode, filterNode, environment)
|
||||||
else -> throw EvaluationException(
|
else -> throw PositionException(
|
||||||
"Expected for-loop with int iterator or foreach-loop with string, list or map iterator or while-loop with bool iterator, found ${iterator.type.name.toLowerCase()}",
|
EnvironmentException(
|
||||||
iteratorNode.position
|
EvaluationException(
|
||||||
|
"Expected for-loop with int iterator or foreach-loop with string, list or map iterator or while-loop with bool iterator, found ${iterator.type.name.toLowerCase()}"
|
||||||
|
),
|
||||||
|
environment
|
||||||
|
), iteratorNode.position
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
environment.popScope()
|
environment.popScope()
|
||||||
@@ -71,7 +77,7 @@ class LoopEvaluator : Evaluator() {
|
|||||||
{ id -> environment.setVariable(id, Value.int(index)) }
|
{ id -> environment.setVariable(id, Value.int(index)) }
|
||||||
)
|
)
|
||||||
|
|
||||||
if(filter(filterNode, environment)) {
|
if (filter(filterNode, environment)) {
|
||||||
outputs.add(defaultEvaluator.evaluate(statementNode, environment))
|
outputs.add(defaultEvaluator.evaluate(statementNode, environment))
|
||||||
index++
|
index++
|
||||||
}
|
}
|
||||||
@@ -134,11 +140,22 @@ class LoopEvaluator : Evaluator() {
|
|||||||
environment: Environment
|
environment: Environment
|
||||||
): EvaluatorOutput {
|
): EvaluatorOutput {
|
||||||
if (parametersNode != Node.NONE) {
|
if (parametersNode != Node.NONE) {
|
||||||
throw EvaluationException("Parameters are not supported in the while-loop", parametersNode.position)
|
throw PositionException(
|
||||||
|
EnvironmentException(
|
||||||
|
EvaluationException("Parameters are not supported in the while-loop"),
|
||||||
|
environment
|
||||||
|
),
|
||||||
|
parametersNode.position
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (filterNode != Node.NONE) {
|
if (filterNode != Node.NONE) {
|
||||||
throw EvaluationException("Filter is not supported in the while-loop", filterNode.position)
|
throw PositionException(
|
||||||
|
EnvironmentException(
|
||||||
|
EvaluationException("Filter is not supported in the while-loop"),
|
||||||
|
environment
|
||||||
|
), filterNode.position
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return output { outputs ->
|
return output { outputs ->
|
||||||
@@ -162,7 +179,15 @@ class LoopEvaluator : Evaluator() {
|
|||||||
if (filterNode != Node.NONE) {
|
if (filterNode != Node.NONE) {
|
||||||
val condition = expressionEvaluator.evaluate(filterNode, environment).value!!
|
val condition = expressionEvaluator.evaluate(filterNode, environment).value!!
|
||||||
if (condition.type != BOOL) {
|
if (condition.type != BOOL) {
|
||||||
throw EvaluationException("Filter condition should be evaluated to bool type", filterNode.position)
|
throw PositionException(
|
||||||
|
EnvironmentException(
|
||||||
|
EvaluationException(
|
||||||
|
"Filter condition should be evaluated to bool type"
|
||||||
|
),
|
||||||
|
environment
|
||||||
|
),
|
||||||
|
filterNode.position
|
||||||
|
)
|
||||||
}
|
}
|
||||||
return condition.value as Boolean
|
return condition.value as Boolean
|
||||||
|
|
||||||
|
|||||||
@@ -5,35 +5,43 @@ import io.smnp.dsl.ast.model.node.MapEntryNode
|
|||||||
import io.smnp.dsl.ast.model.node.MapNode
|
import io.smnp.dsl.ast.model.node.MapNode
|
||||||
import io.smnp.dsl.ast.model.node.Node
|
import io.smnp.dsl.ast.model.node.Node
|
||||||
import io.smnp.environment.Environment
|
import io.smnp.environment.Environment
|
||||||
|
import io.smnp.error.EnvironmentException
|
||||||
import io.smnp.error.EvaluationException
|
import io.smnp.error.EvaluationException
|
||||||
|
import io.smnp.error.PositionException
|
||||||
import io.smnp.evaluation.model.entity.EvaluatorOutput
|
import io.smnp.evaluation.model.entity.EvaluatorOutput
|
||||||
import io.smnp.type.enumeration.DataType.*
|
import io.smnp.type.enumeration.DataType.*
|
||||||
import io.smnp.type.model.Value
|
import io.smnp.type.model.Value
|
||||||
|
|
||||||
class MapEvaluator : Evaluator() {
|
class MapEvaluator : Evaluator() {
|
||||||
private val evaluator = ExpressionEvaluator()
|
private val evaluator = ExpressionEvaluator()
|
||||||
|
|
||||||
override fun supportedNodes() = listOf(MapNode::class)
|
override fun supportedNodes() = listOf(MapNode::class)
|
||||||
|
|
||||||
override fun tryToEvaluate(node: Node, environment: Environment): EvaluatorOutput {
|
override fun tryToEvaluate(node: Node, environment: Environment): EvaluatorOutput {
|
||||||
val value = (node as MapNode).items
|
val value = (node as MapNode).items
|
||||||
.map { it as MapEntryNode }
|
.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()
|
.toMap()
|
||||||
|
|
||||||
return EvaluatorOutput.value(Value.map(value))
|
return EvaluatorOutput.value(Value.map(value))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getKey(keyNode: Node, environment: Environment): Value {
|
private fun getKey(keyNode: Node, environment: Environment): Value {
|
||||||
val key = when(keyNode) {
|
val key = when (keyNode) {
|
||||||
is IdentifierNode -> Value.string(keyNode.token.rawValue)
|
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)) {
|
if (key.type !in listOf(BOOL, INT, NOTE, STRING)) {
|
||||||
throw EvaluationException("Invalid map key's type ${key.type.name.toLowerCase()}", keyNode.position)
|
throw PositionException(
|
||||||
}
|
EnvironmentException(
|
||||||
|
EvaluationException("Invalid map key's type ${key.type.name.toLowerCase()}"),
|
||||||
|
environment
|
||||||
|
),
|
||||||
|
keyNode.position
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return key
|
return key
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,25 +3,35 @@ package io.smnp.evaluation.evaluator
|
|||||||
import io.smnp.dsl.ast.model.node.MinusOperatorNode
|
import io.smnp.dsl.ast.model.node.MinusOperatorNode
|
||||||
import io.smnp.dsl.ast.model.node.Node
|
import io.smnp.dsl.ast.model.node.Node
|
||||||
import io.smnp.environment.Environment
|
import io.smnp.environment.Environment
|
||||||
|
import io.smnp.error.EnvironmentException
|
||||||
import io.smnp.error.EvaluationException
|
import io.smnp.error.EvaluationException
|
||||||
|
import io.smnp.error.PositionException
|
||||||
import io.smnp.evaluation.model.entity.EvaluatorOutput
|
import io.smnp.evaluation.model.entity.EvaluatorOutput
|
||||||
import io.smnp.type.enumeration.DataType
|
import io.smnp.type.enumeration.DataType
|
||||||
import io.smnp.type.model.Value
|
import io.smnp.type.model.Value
|
||||||
|
|
||||||
class MinusOperatorEvaluator : Evaluator() {
|
class MinusOperatorEvaluator : Evaluator() {
|
||||||
override fun supportedNodes() = listOf(MinusOperatorNode::class)
|
override fun supportedNodes() = listOf(MinusOperatorNode::class)
|
||||||
|
|
||||||
override fun tryToEvaluate(node: Node, environment: Environment): EvaluatorOutput {
|
override fun tryToEvaluate(node: Node, environment: Environment): EvaluatorOutput {
|
||||||
val evaluator = ExpressionEvaluator()
|
val evaluator = ExpressionEvaluator()
|
||||||
val (_, operandNode) = (node as MinusOperatorNode)
|
val (_, operandNode) = (node as MinusOperatorNode)
|
||||||
val operand = evaluator.evaluate(operandNode, environment)
|
val operand = evaluator.evaluate(operandNode, environment)
|
||||||
|
|
||||||
return EvaluatorOutput.value(when(operand.value!!.type) {
|
return EvaluatorOutput.value(
|
||||||
|
when (operand.value!!.type) {
|
||||||
DataType.INT -> Value.int(-1 * operand.value.value as Int)
|
DataType.INT -> Value.int(-1 * operand.value.value as Int)
|
||||||
DataType.FLOAT -> Value.float(-1.0f * operand.value.value as Float)
|
DataType.FLOAT -> Value.float(-1.0f * operand.value.value as Float)
|
||||||
DataType.STRING -> Value.string((operand.value.value as String).reversed())
|
DataType.STRING -> Value.string((operand.value.value as String).reversed())
|
||||||
DataType.LIST -> Value.list((operand.value.value as List<Value>).reversed())
|
DataType.LIST -> Value.list((operand.value.value as List<Value>).reversed())
|
||||||
else -> throw EvaluationException("Type ${operand.value.type.name.toLowerCase()} does not support minus operator", node.position)
|
else -> throw PositionException(
|
||||||
})
|
EnvironmentException(
|
||||||
}
|
EvaluationException("Type ${operand.value.type.name.toLowerCase()} does not support minus operator"),
|
||||||
|
environment
|
||||||
|
),
|
||||||
|
node.position
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -3,23 +3,31 @@ package io.smnp.evaluation.evaluator
|
|||||||
import io.smnp.dsl.ast.model.node.Node
|
import io.smnp.dsl.ast.model.node.Node
|
||||||
import io.smnp.dsl.ast.model.node.NotOperatorNode
|
import io.smnp.dsl.ast.model.node.NotOperatorNode
|
||||||
import io.smnp.environment.Environment
|
import io.smnp.environment.Environment
|
||||||
|
import io.smnp.error.EnvironmentException
|
||||||
import io.smnp.error.EvaluationException
|
import io.smnp.error.EvaluationException
|
||||||
|
import io.smnp.error.PositionException
|
||||||
import io.smnp.evaluation.model.entity.EvaluatorOutput
|
import io.smnp.evaluation.model.entity.EvaluatorOutput
|
||||||
import io.smnp.type.enumeration.DataType
|
import io.smnp.type.enumeration.DataType
|
||||||
import io.smnp.type.model.Value
|
import io.smnp.type.model.Value
|
||||||
|
|
||||||
class NotOperatorEvaluator : Evaluator() {
|
class NotOperatorEvaluator : Evaluator() {
|
||||||
val evaluator = assert(ExpressionEvaluator(), "expression")
|
val evaluator = assert(ExpressionEvaluator(), "expression")
|
||||||
override fun supportedNodes() = listOf(NotOperatorNode::class)
|
override fun supportedNodes() = listOf(NotOperatorNode::class)
|
||||||
|
|
||||||
override fun tryToEvaluate(node: Node, environment: Environment): EvaluatorOutput {
|
override fun tryToEvaluate(node: Node, environment: Environment): EvaluatorOutput {
|
||||||
val (_, operandNode) = (node as NotOperatorNode)
|
val (_, operandNode) = (node as NotOperatorNode)
|
||||||
val operand = evaluator.evaluate(operandNode, environment).value!!
|
val operand = evaluator.evaluate(operandNode, environment).value!!
|
||||||
|
|
||||||
if(operand.type != DataType.BOOL) {
|
if (operand.type != DataType.BOOL) {
|
||||||
throw EvaluationException("Only bool types can be negated", operandNode.position)
|
throw PositionException(
|
||||||
}
|
EnvironmentException(
|
||||||
|
EvaluationException("Only bool types can be negated"),
|
||||||
|
environment
|
||||||
|
),
|
||||||
|
operandNode.position
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return EvaluatorOutput.value(Value.bool(!(operand.value as Boolean)))
|
return EvaluatorOutput.value(Value.bool(!(operand.value as Boolean)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,24 +3,32 @@ package io.smnp.evaluation.evaluator
|
|||||||
import io.smnp.dsl.ast.model.node.Node
|
import io.smnp.dsl.ast.model.node.Node
|
||||||
import io.smnp.dsl.ast.model.node.PowerOperatorNode
|
import io.smnp.dsl.ast.model.node.PowerOperatorNode
|
||||||
import io.smnp.environment.Environment
|
import io.smnp.environment.Environment
|
||||||
|
import io.smnp.error.EnvironmentException
|
||||||
import io.smnp.error.EvaluationException
|
import io.smnp.error.EvaluationException
|
||||||
|
import io.smnp.error.PositionException
|
||||||
import io.smnp.evaluation.model.entity.EvaluatorOutput
|
import io.smnp.evaluation.model.entity.EvaluatorOutput
|
||||||
import io.smnp.type.model.Value
|
import io.smnp.type.model.Value
|
||||||
import kotlin.math.pow
|
import kotlin.math.pow
|
||||||
|
|
||||||
class PowerOperatorEvaluator : Evaluator() {
|
class PowerOperatorEvaluator : Evaluator() {
|
||||||
override fun supportedNodes() = listOf(PowerOperatorNode::class)
|
override fun supportedNodes() = listOf(PowerOperatorNode::class)
|
||||||
|
|
||||||
override fun tryToEvaluate(node: Node, environment: Environment): EvaluatorOutput {
|
override fun tryToEvaluate(node: Node, environment: Environment): EvaluatorOutput {
|
||||||
val (lhsNode, _, rhsNode) = (node as PowerOperatorNode)
|
val (lhsNode, _, rhsNode) = (node as PowerOperatorNode)
|
||||||
val evaluator = ExpressionEvaluator()
|
val evaluator = ExpressionEvaluator()
|
||||||
val lhs = evaluator.evaluate(lhsNode, environment).value!!
|
val lhs = evaluator.evaluate(lhsNode, environment).value!!
|
||||||
val rhs = evaluator.evaluate(rhsNode, environment).value!!
|
val rhs = evaluator.evaluate(rhsNode, environment).value!!
|
||||||
|
|
||||||
if(!lhs.type.isNumeric() || !rhs.type.isNumeric()) {
|
if (!lhs.type.isNumeric() || !rhs.type.isNumeric()) {
|
||||||
throw EvaluationException("Operator ** supports only numeric types", node.position)
|
throw PositionException(
|
||||||
}
|
EnvironmentException(
|
||||||
|
EvaluationException("Operator ** supports only numeric types"),
|
||||||
|
environment
|
||||||
|
),
|
||||||
|
node.position
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return EvaluatorOutput.value(Value.float((lhs.value as Number).toFloat().pow((rhs.value as Number).toFloat())))
|
return EvaluatorOutput.value(Value.float((lhs.value as Number).toFloat().pow((rhs.value as Number).toFloat())))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,32 +5,48 @@ import io.smnp.dsl.ast.model.node.ProductOperatorNode
|
|||||||
import io.smnp.dsl.ast.model.node.TokenNode
|
import io.smnp.dsl.ast.model.node.TokenNode
|
||||||
import io.smnp.dsl.token.model.enumeration.TokenType
|
import io.smnp.dsl.token.model.enumeration.TokenType
|
||||||
import io.smnp.environment.Environment
|
import io.smnp.environment.Environment
|
||||||
|
import io.smnp.error.EnvironmentException
|
||||||
import io.smnp.error.EvaluationException
|
import io.smnp.error.EvaluationException
|
||||||
|
import io.smnp.error.PositionException
|
||||||
import io.smnp.error.ShouldNeverReachThisLineException
|
import io.smnp.error.ShouldNeverReachThisLineException
|
||||||
import io.smnp.evaluation.model.entity.EvaluatorOutput
|
import io.smnp.evaluation.model.entity.EvaluatorOutput
|
||||||
import io.smnp.evaluation.util.NumberUnification.unify
|
import io.smnp.evaluation.util.NumberUnification.unify
|
||||||
import io.smnp.type.model.Value
|
import io.smnp.type.model.Value
|
||||||
|
|
||||||
class ProductOperatorEvaluator : Evaluator() {
|
class ProductOperatorEvaluator : Evaluator() {
|
||||||
override fun supportedNodes() = listOf(ProductOperatorNode::class)
|
override fun supportedNodes() = listOf(ProductOperatorNode::class)
|
||||||
|
|
||||||
override fun tryToEvaluate(node: Node, environment: Environment): EvaluatorOutput {
|
override fun tryToEvaluate(node: Node, environment: Environment): EvaluatorOutput {
|
||||||
val evaluator = ExpressionEvaluator()
|
val evaluator = ExpressionEvaluator()
|
||||||
val (lhsNode, opNode, rhsNode) = (node as ProductOperatorNode)
|
val (lhsNode, opNode, rhsNode) = (node as ProductOperatorNode)
|
||||||
val lhs = evaluator.evaluate(lhsNode, environment).value!!
|
val lhs = evaluator.evaluate(lhsNode, environment).value!!
|
||||||
val rhs = evaluator.evaluate(rhsNode, environment).value!!
|
val rhs = evaluator.evaluate(rhsNode, environment).value!!
|
||||||
val operator = (opNode as TokenNode).token.type
|
val operator = (opNode as TokenNode).token.type
|
||||||
|
|
||||||
if (!lhs.type.isNumeric() || !rhs.type.isNumeric()) {
|
if (!lhs.type.isNumeric() || !rhs.type.isNumeric()) {
|
||||||
throw EvaluationException("Operator ${operator.token} supports only numeric types", node.position)
|
throw PositionException(
|
||||||
}
|
EnvironmentException(
|
||||||
|
EvaluationException("Operator ${operator.token} supports only numeric types"),
|
||||||
|
environment
|
||||||
|
),
|
||||||
|
node.position
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return EvaluatorOutput.value(
|
return EvaluatorOutput.value(
|
||||||
when (operator) {
|
when (operator) {
|
||||||
TokenType.ASTERISK -> unify(lhs, rhs, int = { (l, r) -> Value.int(l * r) }, float = { (l, r) -> Value.float(l * r) })
|
TokenType.ASTERISK -> unify(
|
||||||
TokenType.SLASH -> unify(lhs, rhs, int = { (l, r) -> Value.int(l / r) }, float = { (l, r) -> Value.float(l / r) })
|
lhs,
|
||||||
else -> throw ShouldNeverReachThisLineException()
|
rhs,
|
||||||
}
|
int = { (l, r) -> Value.int(l * r) },
|
||||||
)
|
float = { (l, r) -> Value.float(l * r) })
|
||||||
}
|
TokenType.SLASH -> unify(
|
||||||
|
lhs,
|
||||||
|
rhs,
|
||||||
|
int = { (l, r) -> Value.int(l / r) },
|
||||||
|
float = { (l, r) -> Value.float(l / r) })
|
||||||
|
else -> throw ShouldNeverReachThisLineException()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -4,43 +4,51 @@ import io.smnp.dsl.ast.model.node.Node
|
|||||||
import io.smnp.dsl.ast.model.node.RelationOperatorNode
|
import io.smnp.dsl.ast.model.node.RelationOperatorNode
|
||||||
import io.smnp.dsl.ast.model.node.TokenNode
|
import io.smnp.dsl.ast.model.node.TokenNode
|
||||||
import io.smnp.environment.Environment
|
import io.smnp.environment.Environment
|
||||||
|
import io.smnp.error.EnvironmentException
|
||||||
import io.smnp.error.EvaluationException
|
import io.smnp.error.EvaluationException
|
||||||
|
import io.smnp.error.PositionException
|
||||||
import io.smnp.error.ShouldNeverReachThisLineException
|
import io.smnp.error.ShouldNeverReachThisLineException
|
||||||
import io.smnp.evaluation.model.entity.EvaluatorOutput
|
import io.smnp.evaluation.model.entity.EvaluatorOutput
|
||||||
import io.smnp.evaluation.util.NumberUnification.unify
|
import io.smnp.evaluation.util.NumberUnification.unify
|
||||||
import io.smnp.type.model.Value
|
import io.smnp.type.model.Value
|
||||||
|
|
||||||
class RelationOperatorEvaluator : Evaluator() {
|
class RelationOperatorEvaluator : Evaluator() {
|
||||||
override fun supportedNodes() = listOf(RelationOperatorNode::class)
|
override fun supportedNodes() = listOf(RelationOperatorNode::class)
|
||||||
|
|
||||||
override fun tryToEvaluate(node: Node, environment: Environment): EvaluatorOutput {
|
override fun tryToEvaluate(node: Node, environment: Environment): EvaluatorOutput {
|
||||||
val evaluator = ExpressionEvaluator()
|
val evaluator = ExpressionEvaluator()
|
||||||
val (lhsNode, opNode, rhsNode) = (node as RelationOperatorNode)
|
val (lhsNode, opNode, rhsNode) = (node as RelationOperatorNode)
|
||||||
val lhs = evaluator.evaluate(lhsNode, environment).value!!
|
val lhs = evaluator.evaluate(lhsNode, environment).value!!
|
||||||
val rhs = evaluator.evaluate(rhsNode, environment).value!!
|
val rhs = evaluator.evaluate(rhsNode, environment).value!!
|
||||||
val operator = (opNode as TokenNode).token.rawValue
|
val operator = (opNode as TokenNode).token.rawValue
|
||||||
|
|
||||||
if (operator in listOf("==", "!=")) {
|
if (operator in listOf("==", "!=")) {
|
||||||
return EvaluatorOutput.value(
|
return EvaluatorOutput.value(
|
||||||
Value.bool(
|
Value.bool(
|
||||||
if (operator == "==") lhs.value == rhs.value
|
if (operator == "==") lhs.value == rhs.value
|
||||||
else lhs.value != rhs.value
|
else lhs.value != rhs.value
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
)
|
||||||
|
}
|
||||||
|
|
||||||
if (!lhs.type.isNumeric() || !rhs.type.isNumeric()) {
|
if (!lhs.type.isNumeric() || !rhs.type.isNumeric()) {
|
||||||
throw EvaluationException("Operator $operator supports only numeric types", node.position)
|
throw PositionException(
|
||||||
}
|
EnvironmentException(
|
||||||
|
EvaluationException("Operator $operator supports only numeric types"),
|
||||||
|
environment
|
||||||
|
),
|
||||||
|
node.position
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return EvaluatorOutput.value(
|
return EvaluatorOutput.value(
|
||||||
when(operator) {
|
when (operator) {
|
||||||
">" -> unify(lhs, rhs, int = { (l, r) -> Value.bool(l > r) }, float = { (l, r) -> Value.bool(l > r) })
|
">" -> unify(lhs, rhs, int = { (l, r) -> Value.bool(l > r) }, float = { (l, r) -> Value.bool(l > r) })
|
||||||
"<" -> unify(lhs, rhs, int = { (l, r) -> Value.bool(l < r) }, float = { (l, r) -> Value.bool(l < r) })
|
"<" -> unify(lhs, rhs, int = { (l, r) -> Value.bool(l < r) }, float = { (l, r) -> Value.bool(l < r) })
|
||||||
">=" -> unify(lhs, rhs, int = { (l, r) -> Value.bool(l >= r) }, float = { (l, r) -> Value.bool(l >= r) })
|
">=" -> unify(lhs, rhs, int = { (l, r) -> Value.bool(l >= r) }, float = { (l, r) -> Value.bool(l >= r) })
|
||||||
"<=" -> unify(lhs, rhs, int = { (l, r) -> Value.bool(l <= r) }, float = { (l, r) -> Value.bool(l <= r) })
|
"<=" -> unify(lhs, rhs, int = { (l, r) -> Value.bool(l <= r) }, float = { (l, r) -> Value.bool(l <= r) })
|
||||||
else -> throw ShouldNeverReachThisLineException()
|
else -> throw ShouldNeverReachThisLineException()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,7 +5,9 @@ import io.smnp.dsl.ast.model.node.SumOperatorNode
|
|||||||
import io.smnp.dsl.ast.model.node.TokenNode
|
import io.smnp.dsl.ast.model.node.TokenNode
|
||||||
import io.smnp.dsl.token.model.enumeration.TokenType
|
import io.smnp.dsl.token.model.enumeration.TokenType
|
||||||
import io.smnp.environment.Environment
|
import io.smnp.environment.Environment
|
||||||
|
import io.smnp.error.EnvironmentException
|
||||||
import io.smnp.error.EvaluationException
|
import io.smnp.error.EvaluationException
|
||||||
|
import io.smnp.error.PositionException
|
||||||
import io.smnp.error.ShouldNeverReachThisLineException
|
import io.smnp.error.ShouldNeverReachThisLineException
|
||||||
import io.smnp.evaluation.model.entity.EvaluatorOutput
|
import io.smnp.evaluation.model.entity.EvaluatorOutput
|
||||||
import io.smnp.evaluation.util.NumberUnification.unify
|
import io.smnp.evaluation.util.NumberUnification.unify
|
||||||
@@ -24,27 +26,31 @@ class SumOperatorEvaluator : Evaluator() {
|
|||||||
|
|
||||||
return EvaluatorOutput.value(
|
return EvaluatorOutput.value(
|
||||||
when (operator) {
|
when (operator) {
|
||||||
TokenType.PLUS -> plus(lhs, opNode, rhs)
|
TokenType.PLUS -> plus(lhs, opNode, rhs, environment)
|
||||||
TokenType.MINUS -> minus(lhs, opNode, rhs)
|
TokenType.MINUS -> minus(lhs, opNode, rhs, environment)
|
||||||
else -> throw ShouldNeverReachThisLineException()
|
else -> throw ShouldNeverReachThisLineException()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun plus(lhs: Value, plusNode: Node, rhs: Value): Value {
|
private fun plus(lhs: Value, plusNode: Node, rhs: Value, environment: Environment): Value {
|
||||||
return if (areNumeric(lhs, rhs))
|
return if (areNumeric(lhs, rhs))
|
||||||
unify(lhs, rhs, int = { (l, r) -> Value.int(l + r) }, float = { (l, r) -> Value.float(l + r) })
|
unify(lhs, rhs, int = { (l, r) -> Value.int(l + r) }, float = { (l, r) -> Value.float(l + r) })
|
||||||
else if (lhs.type == DataType.STRING)
|
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))
|
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 EvaluationException(
|
else throw PositionException(
|
||||||
"The ${lhs.type.name.toLowerCase()} and ${rhs.type.name.toLowerCase()} are not supported by + operator",
|
EnvironmentException(
|
||||||
plusNode.position
|
EvaluationException(
|
||||||
|
"The ${lhs.type.name.toLowerCase()} and ${rhs.type.name.toLowerCase()} are not supported by + operator"
|
||||||
|
),
|
||||||
|
environment
|
||||||
|
), plusNode.position
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun minus(lhs: Value, minusNode: Node, rhs: Value): Value {
|
private fun minus(lhs: Value, minusNode: Node, rhs: Value, environment: Environment): Value {
|
||||||
return if (areNumeric(lhs, rhs))
|
return if (areNumeric(lhs, rhs))
|
||||||
unify(
|
unify(
|
||||||
lhs,
|
lhs,
|
||||||
@@ -52,7 +58,13 @@ class SumOperatorEvaluator : Evaluator() {
|
|||||||
int = { (l, r) -> Value.int(l - r) },
|
int = { (l, r) -> Value.int(l - r) },
|
||||||
float = { (l, r) -> Value.float(l - r) }
|
float = { (l, r) -> Value.float(l - r) }
|
||||||
)
|
)
|
||||||
else throw EvaluationException("The - operator supports only numeric values", minusNode.position)
|
else throw PositionException(
|
||||||
|
EnvironmentException(
|
||||||
|
EvaluationException("The - operator supports only numeric values"),
|
||||||
|
environment
|
||||||
|
),
|
||||||
|
minusNode.position
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun areNumeric(lhs: Value, rhs: Value) = lhs.type.isNumeric() && rhs.type.isNumeric()
|
private fun areNumeric(lhs: Value, rhs: Value) = lhs.type.isNumeric() && rhs.type.isNumeric()
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package io.smnp.ext
|
package io.smnp.ext
|
||||||
|
|
||||||
|
import io.smnp.error.ModuleException
|
||||||
import org.pf4j.DefaultPluginManager
|
import org.pf4j.DefaultPluginManager
|
||||||
|
|
||||||
object DefaultModuleRegistry : ModuleRegistry {
|
object DefaultModuleRegistry : ModuleRegistry {
|
||||||
@@ -15,7 +16,7 @@ object DefaultModuleRegistry : ModuleRegistry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun requestModuleProviderForPath(path: String): ModuleProvider {
|
override fun requestModuleProviderForPath(path: String): ModuleProvider {
|
||||||
return modules[path] ?: throw RuntimeException("Module $path not found")
|
return modules[path] ?: throw ModuleException("Module $path not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun registeredModules() = modules.keys.toList()
|
override fun registeredModules() = modules.keys.toList()
|
||||||
|
|||||||
@@ -4,50 +4,72 @@ import io.smnp.dsl.ast.parser.RootParser
|
|||||||
import io.smnp.dsl.token.tokenizer.DefaultTokenizer
|
import io.smnp.dsl.token.tokenizer.DefaultTokenizer
|
||||||
import io.smnp.environment.DefaultEnvironment
|
import io.smnp.environment.DefaultEnvironment
|
||||||
import io.smnp.environment.Environment
|
import io.smnp.environment.Environment
|
||||||
|
import io.smnp.error.SmnpException
|
||||||
import io.smnp.evaluation.evaluator.RootEvaluator
|
import io.smnp.evaluation.evaluator.RootEvaluator
|
||||||
import io.smnp.evaluation.model.enumeration.EvaluationResult
|
import io.smnp.evaluation.model.enumeration.EvaluationResult
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.lang.System.err
|
||||||
|
import kotlin.system.exitProcess
|
||||||
|
|
||||||
class DefaultInterpreter : Interpreter {
|
class DefaultInterpreter : Interpreter {
|
||||||
private val tokenizer = DefaultTokenizer()
|
private val tokenizer = DefaultTokenizer()
|
||||||
private val parser = RootParser()
|
private val parser = RootParser()
|
||||||
private val evaluator = RootEvaluator()
|
private val evaluator = RootEvaluator()
|
||||||
|
|
||||||
fun run(code: String, printTokens: Boolean = false, printAst: Boolean = false, dryRun: Boolean = false): Environment {
|
fun run(
|
||||||
val lines = code.split("\n")
|
code: String,
|
||||||
return run(lines, printTokens, printAst, dryRun)
|
printTokens: Boolean = false,
|
||||||
}
|
printAst: Boolean = false,
|
||||||
|
dryRun: Boolean = false
|
||||||
|
): Environment {
|
||||||
|
val lines = code.split("\n")
|
||||||
|
return run(lines, printTokens, printAst, dryRun)
|
||||||
|
}
|
||||||
|
|
||||||
private fun run(lines: List<String>, printTokens: Boolean, printAst: Boolean, dryRun: Boolean): Environment {
|
private fun run(lines: List<String>, printTokens: Boolean, printAst: Boolean, dryRun: Boolean): Environment {
|
||||||
val environment = createEnvironment()
|
try {
|
||||||
val tokens = tokenizer.tokenize(lines)
|
return tryToRun(lines, printTokens, printAst, dryRun)
|
||||||
val ast = parser.parse(tokens)
|
} catch (e: SmnpException) {
|
||||||
|
printError(e)
|
||||||
|
exitProcess(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(printTokens) println(tokens)
|
private fun printError(e: SmnpException) {
|
||||||
if(printAst) ast.node.pretty()
|
err.println(e.friendlyName)
|
||||||
|
err.println(e.message)
|
||||||
|
}
|
||||||
|
|
||||||
if(!dryRun) {
|
private fun tryToRun(lines: List<String>, printTokens: Boolean, printAst: Boolean, dryRun: Boolean): Environment {
|
||||||
val result = evaluator.evaluate(ast.node, environment)
|
val environment = createEnvironment()
|
||||||
|
val tokens = tokenizer.tokenize(lines)
|
||||||
|
val ast = parser.parse(tokens)
|
||||||
|
|
||||||
if(result.result == EvaluationResult.FAILED) {
|
if (printTokens) println(tokens)
|
||||||
throw RuntimeException("Evaluation failed")
|
if (printAst) ast.node.pretty()
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return environment
|
if (!dryRun) {
|
||||||
}
|
val result = evaluator.evaluate(ast.node, environment)
|
||||||
|
|
||||||
private fun createEnvironment(): Environment {
|
if (result.result == EvaluationResult.FAILED) {
|
||||||
val environment = DefaultEnvironment()
|
throw RuntimeException("Evaluation failed")
|
||||||
environment.loadModule("smnp.lang")
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return environment
|
return environment
|
||||||
}
|
}
|
||||||
|
|
||||||
fun run(file: File, printTokens: Boolean = false, printAst: Boolean = false, dryRun: Boolean = false): Environment {
|
private fun createEnvironment(): Environment {
|
||||||
val lines = file.readLines()
|
val environment = DefaultEnvironment()
|
||||||
return run(lines, printTokens, printAst, dryRun)
|
environment.loadModule("smnp.lang")
|
||||||
}
|
|
||||||
|
|
||||||
override fun run(code: String) = run(code, printTokens = false, printAst = false, dryRun = false)
|
return environment
|
||||||
|
}
|
||||||
|
|
||||||
|
fun run(file: File, printTokens: Boolean = false, printAst: Boolean = false, dryRun: Boolean = false): Environment {
|
||||||
|
val lines = file.readLines()
|
||||||
|
return run(lines, printTokens, printAst, dryRun)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun run(code: String) = run(code, printTokens = false, printAst = false, dryRun = false)
|
||||||
}
|
}
|
||||||
@@ -3,7 +3,7 @@ package io.smnp.ext.lang.method
|
|||||||
import io.smnp.callable.method.Method
|
import io.smnp.callable.method.Method
|
||||||
import io.smnp.callable.method.MethodDefinitionTool
|
import io.smnp.callable.method.MethodDefinitionTool
|
||||||
import io.smnp.callable.signature.Signature.Companion.simple
|
import io.smnp.callable.signature.Signature.Companion.simple
|
||||||
import io.smnp.error.RuntimeException
|
import io.smnp.error.EvaluationException
|
||||||
import io.smnp.type.enumeration.DataType.INT
|
import io.smnp.type.enumeration.DataType.INT
|
||||||
import io.smnp.type.enumeration.DataType.STRING
|
import io.smnp.type.enumeration.DataType.STRING
|
||||||
import io.smnp.type.matcher.Matcher.Companion.ofType
|
import io.smnp.type.matcher.Matcher.Companion.ofType
|
||||||
@@ -12,7 +12,7 @@ import io.smnp.type.model.Value
|
|||||||
class CharAtMethod : Method(ofType(STRING),"charAt") {
|
class CharAtMethod : Method(ofType(STRING),"charAt") {
|
||||||
override fun define(new: MethodDefinitionTool) {
|
override fun define(new: MethodDefinitionTool) {
|
||||||
new method simple(ofType(INT)) body { _, obj, (index) ->
|
new method simple(ofType(INT)) body { _, obj, (index) ->
|
||||||
Value.string((obj.value!! as String).getOrNull(index.value!! as Int)?.toString() ?: throw RuntimeException("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"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,7 +3,7 @@ package io.smnp.ext.lang.method
|
|||||||
import io.smnp.callable.method.Method
|
import io.smnp.callable.method.Method
|
||||||
import io.smnp.callable.method.MethodDefinitionTool
|
import io.smnp.callable.method.MethodDefinitionTool
|
||||||
import io.smnp.callable.signature.Signature.Companion.simple
|
import io.smnp.callable.signature.Signature.Companion.simple
|
||||||
import io.smnp.error.RuntimeException
|
import io.smnp.error.EvaluationException
|
||||||
import io.smnp.type.enumeration.DataType.INT
|
import io.smnp.type.enumeration.DataType.INT
|
||||||
import io.smnp.type.enumeration.DataType.LIST
|
import io.smnp.type.enumeration.DataType.LIST
|
||||||
import io.smnp.type.matcher.Matcher.Companion.ofType
|
import io.smnp.type.matcher.Matcher.Companion.ofType
|
||||||
@@ -16,7 +16,7 @@ class ListAccessMethod : Method(ofType(LIST), "get") {
|
|||||||
val i = index.value!! as Int
|
val i = index.value!! as Int
|
||||||
|
|
||||||
if(i >= list.size) {
|
if(i >= list.size) {
|
||||||
throw RuntimeException("Index '$i' runs out of array bounds")
|
throw EvaluationException("Index '$i' runs out of array bounds")
|
||||||
}
|
}
|
||||||
|
|
||||||
list[i]
|
list[i]
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package io.smnp.ext.lang.method
|
|||||||
import io.smnp.callable.method.Method
|
import io.smnp.callable.method.Method
|
||||||
import io.smnp.callable.method.MethodDefinitionTool
|
import io.smnp.callable.method.MethodDefinitionTool
|
||||||
import io.smnp.callable.signature.Signature.Companion.simple
|
import io.smnp.callable.signature.Signature.Companion.simple
|
||||||
import io.smnp.error.RuntimeException
|
import io.smnp.error.EvaluationException
|
||||||
import io.smnp.type.enumeration.DataType.MAP
|
import io.smnp.type.enumeration.DataType.MAP
|
||||||
import io.smnp.type.matcher.Matcher.Companion.allTypes
|
import io.smnp.type.matcher.Matcher.Companion.allTypes
|
||||||
import io.smnp.type.matcher.Matcher.Companion.ofType
|
import io.smnp.type.matcher.Matcher.Companion.ofType
|
||||||
@@ -13,7 +13,7 @@ class MapAccessMethod : Method(ofType(MAP), "get") {
|
|||||||
override fun define(new: MethodDefinitionTool) {
|
override fun define(new: MethodDefinitionTool) {
|
||||||
new method simple(allTypes()) body { _, obj, (key) ->
|
new method simple(allTypes()) body { _, obj, (key) ->
|
||||||
val map = (obj.value!! as Map<Value, Value>)
|
val map = (obj.value!! as Map<Value, Value>)
|
||||||
map[key] ?: throw RuntimeException("Key '${key.value!!}' not found")
|
map[key] ?: throw EvaluationException("Key '${key.value!!}' not found")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user