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.error.FunctionInvocationException
|
||||
import io.smnp.error.RuntimeException
|
||||
import io.smnp.type.model.Value
|
||||
import io.smnp.type.module.Module
|
||||
|
||||
@@ -25,7 +24,7 @@ abstract class Function(val name: String) {
|
||||
val (definition, args) = definitions
|
||||
.map { Pair(it, it.signature.parse(arguments.toList())) }
|
||||
.firstOrNull { (_, args) -> args.signatureMatched }
|
||||
?: throw FunctionInvocationException(this, arguments)
|
||||
?: throw FunctionInvocationException(this, arguments, environment)
|
||||
|
||||
return definition.body(environment, args.arguments)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package io.smnp.callable.method
|
||||
|
||||
import io.smnp.environment.Environment
|
||||
import io.smnp.error.MethodInvocationException
|
||||
import io.smnp.error.RuntimeException
|
||||
import io.smnp.type.matcher.Matcher
|
||||
import io.smnp.type.model.Value
|
||||
import io.smnp.type.module.Module
|
||||
@@ -28,7 +27,7 @@ abstract class Method(val typeMatcher: Matcher, val name: String) {
|
||||
val (definition, args) = definitions
|
||||
.map { Pair(it, it.signature.parse(arguments.toList())) }
|
||||
.firstOrNull { (_, args) -> args.signatureMatched }
|
||||
?: throw MethodInvocationException(this, obj, arguments)
|
||||
?: throw MethodInvocationException(this, obj, arguments, environment)
|
||||
|
||||
return definition.body(environment, obj, args.arguments)
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ interface Environment {
|
||||
fun invokeFunction(name: String, arguments: List<Value>): Value
|
||||
fun invokeMethod(obj: Value, name: String, arguments: List<Value>): Value
|
||||
fun printCallStack()
|
||||
fun stackTrace(): String
|
||||
fun defineFunction(function: Function)
|
||||
fun defineMethod(method: Method)
|
||||
fun pushScope(scope: MutableMap<String, Value> = mutableMapOf())
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
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.signature.ActualSignatureFormatter.format
|
||||
import io.smnp.environment.Environment
|
||||
import io.smnp.type.model.Value
|
||||
|
||||
class FunctionInvocationException(private val function: Function, private val passedArguments: Array<out Value>) : Exception() {
|
||||
override fun toString() = "Invalid signature: ${function.name}${format(passedArguments)}\nAllowed signatures:\n${function.signature}"
|
||||
}
|
||||
class FunctionInvocationException(
|
||||
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.signature.ActualSignatureFormatter.format
|
||||
import io.smnp.environment.Environment
|
||||
import io.smnp.type.model.Value
|
||||
|
||||
class MethodInvocationException(private val method: Method, private val obj: Value, private val passedArguments: Array<out Value>) : Exception() {
|
||||
override fun toString() = "Invalid signature: $obj.${method.name}${format(passedArguments)}\nAllowed signatures:\n${method.signature}"
|
||||
}
|
||||
class MethodInvocationException(
|
||||
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() {
|
||||
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.collection.Stack
|
||||
import io.smnp.error.EvaluationException
|
||||
import io.smnp.type.model.Value
|
||||
import io.smnp.type.module.Module
|
||||
|
||||
@@ -24,7 +25,7 @@ data class CallStackFrame(
|
||||
}
|
||||
|
||||
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() {
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
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.dsl.ast.model.node.*
|
||||
import io.smnp.error.InvalidSignatureException
|
||||
import io.smnp.error.ShouldNeverReachThisLineException
|
||||
import io.smnp.type.enumeration.DataType
|
||||
import io.smnp.type.matcher.Matcher
|
||||
@@ -42,7 +43,7 @@ object FunctionSignatureParser {
|
||||
}
|
||||
}.eachCount().forEach {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
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
|
||||
@@ -107,7 +108,7 @@ object FunctionSignatureParser {
|
||||
)
|
||||
}
|
||||
|
||||
throw RuntimeException("Unknown type")
|
||||
throw ShouldNeverReachThisLineException()
|
||||
}
|
||||
|
||||
private fun listSpecifier(listSpecifierNode: TypeSpecifierNode): Matcher {
|
||||
|
||||
@@ -14,7 +14,7 @@ class ConditionParser : Parser() {
|
||||
assert(SubexpressionParser(), "expression"),
|
||||
terminal(TokenType.CLOSE_PAREN),
|
||||
assert(StatementParser(), "statement")
|
||||
) { (ifToken, condition, trueBranch) ->
|
||||
) { (ifToken, _, condition, _, trueBranch) ->
|
||||
ConditionNode(ifToken, condition, trueBranch, Node.NONE, Node.NONE)
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ class ConditionParser : Parser() {
|
||||
assert(StatementParser(), "statement"),
|
||||
terminal(TokenType.ELSE),
|
||||
assert(StatementParser(), "statement")
|
||||
) { (ifToken, condition, trueBranch, elseToken, falseBranch) ->
|
||||
) { (ifToken, _, condition, _, trueBranch, elseToken, falseBranch) ->
|
||||
ConditionNode(ifToken, condition, trueBranch, elseToken, falseBranch)
|
||||
}
|
||||
|
||||
@@ -36,3 +36,7 @@ class ConditionParser : Parser() {
|
||||
).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)) {
|
||||
begin, methods, end -> BlockNode(begin, methods, end)
|
||||
}, "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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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.enumeration.TokenType
|
||||
import io.smnp.error.InvalidSyntaxException
|
||||
import io.smnp.error.PositionException
|
||||
|
||||
abstract class Parser {
|
||||
fun parse(input: TokenList): ParserOutput {
|
||||
@@ -178,7 +179,7 @@ abstract class Parser {
|
||||
val output = parser.parse(input)
|
||||
|
||||
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
|
||||
|
||||
@@ -6,10 +6,10 @@ data class TokenPosition(val line: Int, val beginCol: Int, val endCol: Int) {
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "[line ${line+1}, col ${beginCol}]"
|
||||
return "[line ${line+1}, col ${beginCol+1}]"
|
||||
}
|
||||
|
||||
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.separated
|
||||
import io.smnp.error.InvalidSyntaxException
|
||||
import io.smnp.error.PositionException
|
||||
|
||||
class DefaultTokenizer : Tokenizer {
|
||||
private val tokenizers = listOf(
|
||||
@@ -91,7 +92,7 @@ class DefaultTokenizer : Tokenizer {
|
||||
val output = tokenize(line, current, index)
|
||||
|
||||
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
|
||||
|
||||
@@ -3,6 +3,7 @@ package io.smnp.environment
|
||||
import io.smnp.callable.function.Function
|
||||
import io.smnp.callable.method.Method
|
||||
import io.smnp.callable.signature.ActualSignatureFormatter.format
|
||||
import io.smnp.error.EvaluationException
|
||||
import io.smnp.ext.DefaultModuleRegistry.requestModuleProviderForPath
|
||||
import io.smnp.ext.ModuleProvider
|
||||
import io.smnp.interpreter.LanguageModuleInterpreter
|
||||
@@ -49,11 +50,11 @@ class DefaultEnvironment : Environment {
|
||||
override fun invokeFunction(name: String, arguments: List<Value>): Value {
|
||||
val foundFunctions = rootModule.findFunction(name)
|
||||
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) {
|
||||
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 {
|
||||
val foundMethods = rootModule.findMethod(obj, name)
|
||||
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) {
|
||||
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]
|
||||
@@ -89,6 +98,10 @@ class DefaultEnvironment : Environment {
|
||||
callStack.pretty()
|
||||
}
|
||||
|
||||
override fun stackTrace(): String {
|
||||
return callStack.stackTrace();
|
||||
}
|
||||
|
||||
override fun defineFunction(function: 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
|
||||
|
||||
import io.smnp.dsl.token.model.entity.TokenPosition
|
||||
|
||||
class InvalidSyntaxException(message: String?, val position: TokenPosition?) : Exception(message) {
|
||||
override val message: String?
|
||||
get() = super.message + if(position != null) " $position" else ""
|
||||
}
|
||||
class InvalidSyntaxException(message: String?) : SmnpException("Syntax error", message)
|
||||
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,7 +2,10 @@ package io.smnp.evaluation.evaluator
|
||||
|
||||
import io.smnp.dsl.ast.model.node.*
|
||||
import io.smnp.environment.Environment
|
||||
import io.smnp.error.EnvironmentException
|
||||
import io.smnp.error.EvaluationException
|
||||
import io.smnp.error.MethodInvocationException
|
||||
import io.smnp.error.PositionException
|
||||
import io.smnp.evaluation.model.entity.EvaluatorOutput
|
||||
|
||||
class AccessOperatorEvaluator : Evaluator() {
|
||||
@@ -16,16 +19,36 @@ class AccessOperatorEvaluator : Evaluator() {
|
||||
return when (rhsNode) {
|
||||
is IdentifierNode -> {
|
||||
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!! }
|
||||
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)
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
throw EvaluationException("Invalid property access type - only property name and method call are allowed", rhsNode.position)
|
||||
throw PositionException(
|
||||
EnvironmentException(
|
||||
EvaluationException("Invalid property access type - only property name and method call are allowed"),
|
||||
environment
|
||||
), 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.Node
|
||||
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.type.enumeration.DataType
|
||||
|
||||
@@ -19,8 +21,11 @@ class AssignmentOperatorEvaluator : Evaluator() {
|
||||
val value = evaluator.evaluate(valueNode, environment).value!!
|
||||
|
||||
if (value.type == DataType.VOID) {
|
||||
throw EvaluationException(
|
||||
"Right hand side expression of assignment operation has returned nothing",
|
||||
throw PositionException(
|
||||
EnvironmentException(
|
||||
EvaluationException("Right hand side expression of assignment operation has returned nothing"),
|
||||
environment
|
||||
),
|
||||
valueNode.position
|
||||
)
|
||||
}
|
||||
|
||||
@@ -4,21 +4,30 @@ import io.smnp.dsl.ast.model.node.ConditionNode
|
||||
import io.smnp.dsl.ast.model.node.Node
|
||||
import io.smnp.dsl.ast.model.node.NoneNode
|
||||
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.type.enumeration.DataType
|
||||
|
||||
class ConditionEvaluator : Evaluator() {
|
||||
private val expressionEvaluator = ExpressionEvaluator()
|
||||
private val defaultEvaluator = DefaultEvaluator()
|
||||
|
||||
override fun supportedNodes() = listOf(ConditionNode::class)
|
||||
|
||||
override fun tryToEvaluate(node: Node, environment: Environment): EvaluatorOutput {
|
||||
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) {
|
||||
throw EvaluationException("Condition should be of bool type, found '${condition.value}'", conditionNode.position)
|
||||
throw PositionException(
|
||||
EnvironmentException(
|
||||
EvaluationException("Condition should be of bool type, found '${condition.value}'"),
|
||||
environment
|
||||
),
|
||||
conditionNode.position
|
||||
)
|
||||
}
|
||||
|
||||
if (condition.value!! as Boolean) {
|
||||
|
||||
@@ -2,7 +2,9 @@ package io.smnp.evaluation.evaluator
|
||||
|
||||
import io.smnp.dsl.ast.model.node.Node
|
||||
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.enumeration.EvaluationResult
|
||||
import kotlin.reflect.KClass
|
||||
@@ -62,7 +64,10 @@ abstract class Evaluator {
|
||||
val output = evaluator.evaluate(node, environment)
|
||||
|
||||
if (output.result == EvaluationResult.FAILED) {
|
||||
throw EvaluationException("Expected $expected", node.position)
|
||||
throw PositionException(
|
||||
EnvironmentException(EvaluationException("Expected $expected"), environment),
|
||||
node.position
|
||||
)
|
||||
}
|
||||
|
||||
return output
|
||||
|
||||
@@ -4,6 +4,9 @@ import io.smnp.callable.method.CustomMethod
|
||||
import io.smnp.callable.util.FunctionSignatureParser
|
||||
import io.smnp.dsl.ast.model.node.*
|
||||
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
|
||||
|
||||
class ExtendEvaluator : Evaluator() {
|
||||
@@ -15,8 +18,14 @@ class ExtendEvaluator : Evaluator() {
|
||||
val identifier = (identifierNode as IdentifierNode).token.rawValue
|
||||
|
||||
methodsNode.children
|
||||
.map { CustomMethod.create(type, identifier, it as FunctionDefinitionNode) }
|
||||
.forEach { environment.defineMethod(it) }
|
||||
.map { it to CustomMethod.create(type, identifier, it as FunctionDefinitionNode) }
|
||||
.forEach {
|
||||
try {
|
||||
environment.defineMethod(it.second)
|
||||
} catch (e: SmnpException) {
|
||||
throw PositionException(EnvironmentException(e, environment), it.first.position)
|
||||
}
|
||||
}
|
||||
|
||||
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.Node
|
||||
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
|
||||
|
||||
class FunctionCallEvaluator : Evaluator() {
|
||||
@@ -16,6 +20,12 @@ class FunctionCallEvaluator : Evaluator() {
|
||||
val identifier = (identifierNode as IdentifierNode).token.rawValue
|
||||
val arguments = (argsNode as FunctionCallArgumentsNode).items.map { evaluator.evaluate(it, environment).value!! }
|
||||
|
||||
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.Node
|
||||
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
|
||||
|
||||
class FunctionDefinitionEvaluator : Evaluator() {
|
||||
@@ -11,7 +14,13 @@ class FunctionDefinitionEvaluator : Evaluator() {
|
||||
|
||||
override fun tryToEvaluate(node: Node, environment: Environment): EvaluatorOutput {
|
||||
val function = CustomFunction.create(node as FunctionDefinitionNode)
|
||||
|
||||
try {
|
||||
environment.defineFunction(function)
|
||||
} catch(e: SmnpException) {
|
||||
throw PositionException(EnvironmentException(e, environment), node.position)
|
||||
}
|
||||
|
||||
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.Node
|
||||
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
|
||||
|
||||
class IdentifierEvaluator : Evaluator() {
|
||||
@@ -10,6 +13,11 @@ class IdentifierEvaluator : Evaluator() {
|
||||
|
||||
override fun tryToEvaluate(node: Node, environment: Environment): EvaluatorOutput {
|
||||
val identifier = (node as IdentifierNode).token.rawValue
|
||||
|
||||
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.Node
|
||||
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
|
||||
|
||||
class ImportEvaluator : Evaluator() {
|
||||
@@ -11,7 +14,12 @@ class ImportEvaluator : Evaluator() {
|
||||
|
||||
override fun tryToEvaluate(node: Node, environment: Environment): EvaluatorOutput {
|
||||
val path = (node as ImportNode).path.joinToString(".") { (it as IdentifierNode).token.rawValue }
|
||||
|
||||
try {
|
||||
environment.loadModule(path)
|
||||
} catch(e: SmnpException) {
|
||||
throw PositionException(EnvironmentException(e, environment), node.position)
|
||||
}
|
||||
|
||||
return EvaluatorOutput.ok()
|
||||
}
|
||||
|
||||
@@ -5,7 +5,9 @@ import io.smnp.dsl.ast.model.node.Node
|
||||
import io.smnp.dsl.ast.model.node.TokenNode
|
||||
import io.smnp.dsl.token.model.enumeration.TokenType
|
||||
import io.smnp.environment.Environment
|
||||
import io.smnp.error.EnvironmentException
|
||||
import io.smnp.error.EvaluationException
|
||||
import io.smnp.error.PositionException
|
||||
import io.smnp.error.ShouldNeverReachThisLineException
|
||||
import io.smnp.evaluation.model.entity.EvaluatorOutput
|
||||
import io.smnp.type.enumeration.DataType
|
||||
@@ -22,17 +24,31 @@ class LogicOperatorEvaluator : Evaluator() {
|
||||
val operator = (opNode as TokenNode).token.type
|
||||
|
||||
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) {
|
||||
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.OR -> Value.bool((lhs.value as Boolean) || (rhs.value as Boolean))
|
||||
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.Node
|
||||
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.enumeration.EvaluationResult
|
||||
import io.smnp.type.enumeration.DataType.*
|
||||
@@ -26,9 +28,13 @@ class LoopEvaluator : Evaluator() {
|
||||
LIST -> evaluateForList(iterator, parametersNode, statementNode, filterNode, environment)
|
||||
MAP -> evaluateForMap(iterator, parametersNode, statementNode, filterNode, environment)
|
||||
BOOL -> evaluateForBool(iteratorNode, parametersNode, statementNode, filterNode, environment)
|
||||
else -> throw 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()}",
|
||||
iteratorNode.position
|
||||
else -> throw PositionException(
|
||||
EnvironmentException(
|
||||
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()
|
||||
@@ -134,11 +140,22 @@ class LoopEvaluator : Evaluator() {
|
||||
environment: Environment
|
||||
): EvaluatorOutput {
|
||||
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) {
|
||||
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 ->
|
||||
@@ -162,7 +179,15 @@ class LoopEvaluator : Evaluator() {
|
||||
if (filterNode != Node.NONE) {
|
||||
val condition = expressionEvaluator.evaluate(filterNode, environment).value!!
|
||||
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
|
||||
|
||||
|
||||
@@ -5,7 +5,9 @@ import io.smnp.dsl.ast.model.node.MapEntryNode
|
||||
import io.smnp.dsl.ast.model.node.MapNode
|
||||
import io.smnp.dsl.ast.model.node.Node
|
||||
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.type.enumeration.DataType.*
|
||||
import io.smnp.type.model.Value
|
||||
@@ -31,7 +33,13 @@ class MapEvaluator : Evaluator() {
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@@ -3,7 +3,9 @@ package io.smnp.evaluation.evaluator
|
||||
import io.smnp.dsl.ast.model.node.MinusOperatorNode
|
||||
import io.smnp.dsl.ast.model.node.Node
|
||||
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.type.enumeration.DataType
|
||||
import io.smnp.type.model.Value
|
||||
@@ -16,12 +18,20 @@ class MinusOperatorEvaluator : Evaluator() {
|
||||
val (_, operandNode) = (node as MinusOperatorNode)
|
||||
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.FLOAT -> Value.float(-1.0f * operand.value.value as Float)
|
||||
DataType.STRING -> Value.string((operand.value.value as String).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,7 +3,9 @@ package io.smnp.evaluation.evaluator
|
||||
import io.smnp.dsl.ast.model.node.Node
|
||||
import io.smnp.dsl.ast.model.node.NotOperatorNode
|
||||
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.type.enumeration.DataType
|
||||
import io.smnp.type.model.Value
|
||||
@@ -17,7 +19,13 @@ class NotOperatorEvaluator : Evaluator() {
|
||||
val operand = evaluator.evaluate(operandNode, environment).value!!
|
||||
|
||||
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)))
|
||||
|
||||
@@ -3,7 +3,9 @@ package io.smnp.evaluation.evaluator
|
||||
import io.smnp.dsl.ast.model.node.Node
|
||||
import io.smnp.dsl.ast.model.node.PowerOperatorNode
|
||||
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.type.model.Value
|
||||
import kotlin.math.pow
|
||||
@@ -18,7 +20,13 @@ class PowerOperatorEvaluator : Evaluator() {
|
||||
val rhs = evaluator.evaluate(rhsNode, environment).value!!
|
||||
|
||||
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())))
|
||||
|
||||
@@ -5,7 +5,9 @@ import io.smnp.dsl.ast.model.node.ProductOperatorNode
|
||||
import io.smnp.dsl.ast.model.node.TokenNode
|
||||
import io.smnp.dsl.token.model.enumeration.TokenType
|
||||
import io.smnp.environment.Environment
|
||||
import io.smnp.error.EnvironmentException
|
||||
import io.smnp.error.EvaluationException
|
||||
import io.smnp.error.PositionException
|
||||
import io.smnp.error.ShouldNeverReachThisLineException
|
||||
import io.smnp.evaluation.model.entity.EvaluatorOutput
|
||||
import io.smnp.evaluation.util.NumberUnification.unify
|
||||
@@ -22,13 +24,27 @@ class ProductOperatorEvaluator : Evaluator() {
|
||||
val operator = (opNode as TokenNode).token.type
|
||||
|
||||
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(
|
||||
when (operator) {
|
||||
TokenType.ASTERISK -> unify(lhs, 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) })
|
||||
TokenType.ASTERISK -> unify(
|
||||
lhs,
|
||||
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,7 +4,9 @@ import io.smnp.dsl.ast.model.node.Node
|
||||
import io.smnp.dsl.ast.model.node.RelationOperatorNode
|
||||
import io.smnp.dsl.ast.model.node.TokenNode
|
||||
import io.smnp.environment.Environment
|
||||
import io.smnp.error.EnvironmentException
|
||||
import io.smnp.error.EvaluationException
|
||||
import io.smnp.error.PositionException
|
||||
import io.smnp.error.ShouldNeverReachThisLineException
|
||||
import io.smnp.evaluation.model.entity.EvaluatorOutput
|
||||
import io.smnp.evaluation.util.NumberUnification.unify
|
||||
@@ -30,7 +32,13 @@ class RelationOperatorEvaluator : Evaluator() {
|
||||
}
|
||||
|
||||
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(
|
||||
|
||||
@@ -5,7 +5,9 @@ import io.smnp.dsl.ast.model.node.SumOperatorNode
|
||||
import io.smnp.dsl.ast.model.node.TokenNode
|
||||
import io.smnp.dsl.token.model.enumeration.TokenType
|
||||
import io.smnp.environment.Environment
|
||||
import io.smnp.error.EnvironmentException
|
||||
import io.smnp.error.EvaluationException
|
||||
import io.smnp.error.PositionException
|
||||
import io.smnp.error.ShouldNeverReachThisLineException
|
||||
import io.smnp.evaluation.model.entity.EvaluatorOutput
|
||||
import io.smnp.evaluation.util.NumberUnification.unify
|
||||
@@ -24,27 +26,31 @@ class SumOperatorEvaluator : Evaluator() {
|
||||
|
||||
return EvaluatorOutput.value(
|
||||
when (operator) {
|
||||
TokenType.PLUS -> plus(lhs, opNode, rhs)
|
||||
TokenType.MINUS -> minus(lhs, opNode, rhs)
|
||||
TokenType.PLUS -> plus(lhs, opNode, rhs, environment)
|
||||
TokenType.MINUS -> minus(lhs, opNode, rhs, environment)
|
||||
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))
|
||||
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())
|
||||
else if (areLists(lhs, rhs))
|
||||
Value.list(lhs.value!! as List<Value> + rhs.value!! as List<Value>)
|
||||
else throw EvaluationException(
|
||||
"The ${lhs.type.name.toLowerCase()} and ${rhs.type.name.toLowerCase()} are not supported by + operator",
|
||||
plusNode.position
|
||||
else throw PositionException(
|
||||
EnvironmentException(
|
||||
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))
|
||||
unify(
|
||||
lhs,
|
||||
@@ -52,7 +58,13 @@ class SumOperatorEvaluator : Evaluator() {
|
||||
int = { (l, r) -> Value.int(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()
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.smnp.ext
|
||||
|
||||
import io.smnp.error.ModuleException
|
||||
import org.pf4j.DefaultPluginManager
|
||||
|
||||
object DefaultModuleRegistry : ModuleRegistry {
|
||||
@@ -15,7 +16,7 @@ object DefaultModuleRegistry : ModuleRegistry {
|
||||
}
|
||||
|
||||
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()
|
||||
|
||||
@@ -4,21 +4,43 @@ import io.smnp.dsl.ast.parser.RootParser
|
||||
import io.smnp.dsl.token.tokenizer.DefaultTokenizer
|
||||
import io.smnp.environment.DefaultEnvironment
|
||||
import io.smnp.environment.Environment
|
||||
import io.smnp.error.SmnpException
|
||||
import io.smnp.evaluation.evaluator.RootEvaluator
|
||||
import io.smnp.evaluation.model.enumeration.EvaluationResult
|
||||
import java.io.File
|
||||
import java.lang.System.err
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
class DefaultInterpreter : Interpreter {
|
||||
private val tokenizer = DefaultTokenizer()
|
||||
private val parser = RootParser()
|
||||
private val evaluator = RootEvaluator()
|
||||
|
||||
fun run(code: String, printTokens: Boolean = false, printAst: Boolean = false, dryRun: Boolean = false): Environment {
|
||||
fun run(
|
||||
code: String,
|
||||
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 {
|
||||
try {
|
||||
return tryToRun(lines, printTokens, printAst, dryRun)
|
||||
} catch (e: SmnpException) {
|
||||
printError(e)
|
||||
exitProcess(1)
|
||||
}
|
||||
}
|
||||
|
||||
private fun printError(e: SmnpException) {
|
||||
err.println(e.friendlyName)
|
||||
err.println(e.message)
|
||||
}
|
||||
|
||||
private fun tryToRun(lines: List<String>, printTokens: Boolean, printAst: Boolean, dryRun: Boolean): Environment {
|
||||
val environment = createEnvironment()
|
||||
val tokens = tokenizer.tokenize(lines)
|
||||
val ast = parser.parse(tokens)
|
||||
|
||||
@@ -3,7 +3,7 @@ package io.smnp.ext.lang.method
|
||||
import io.smnp.callable.method.Method
|
||||
import io.smnp.callable.method.MethodDefinitionTool
|
||||
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.STRING
|
||||
import io.smnp.type.matcher.Matcher.Companion.ofType
|
||||
@@ -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 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.MethodDefinitionTool
|
||||
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.LIST
|
||||
import io.smnp.type.matcher.Matcher.Companion.ofType
|
||||
@@ -16,7 +16,7 @@ class ListAccessMethod : Method(ofType(LIST), "get") {
|
||||
val i = index.value!! as Int
|
||||
|
||||
if(i >= list.size) {
|
||||
throw RuntimeException("Index '$i' runs out of array bounds")
|
||||
throw EvaluationException("Index '$i' runs out of array bounds")
|
||||
}
|
||||
|
||||
list[i]
|
||||
|
||||
@@ -3,7 +3,7 @@ package io.smnp.ext.lang.method
|
||||
import io.smnp.callable.method.Method
|
||||
import io.smnp.callable.method.MethodDefinitionTool
|
||||
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.matcher.Matcher.Companion.allTypes
|
||||
import io.smnp.type.matcher.Matcher.Companion.ofType
|
||||
@@ -13,7 +13,7 @@ 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 RuntimeException("Key '${key.value!!}' not found")
|
||||
map[key] ?: throw EvaluationException("Key '${key.value!!}' not found")
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user