diff --git a/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/Expression.kt b/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/Expression.kt index e6f3d0fb..2fb0db71 100644 --- a/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/Expression.kt +++ b/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/Expression.kt @@ -3,16 +3,16 @@ package io.github.alexzhirkevich.skriptie import io.github.alexzhirkevich.skriptie.common.OpGetVariable import io.github.alexzhirkevich.skriptie.common.OpIndex -public fun interface Expression { - public fun invokeRaw(context: C): Any? +public fun interface Expression { + public fun invokeRaw(context: ScriptRuntime): Any? } -public operator fun Expression.invoke(context: C): Any? = +public operator fun Expression.invoke(context: ScriptRuntime): Any? = context.fromKotlin(invokeRaw(context)) -internal fun Expression<*>.isAssignable() : Boolean { +internal fun Expression.isAssignable() : Boolean { return this is OpGetVariable && assignmentType == null || this is OpIndex && variable is OpGetVariable } \ No newline at end of file diff --git a/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/InterpretationContext.kt b/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/InterpretationContext.kt index 3f5a1d7a..64af8b70 100644 --- a/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/InterpretationContext.kt +++ b/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/InterpretationContext.kt @@ -1,17 +1,17 @@ package io.github.alexzhirkevich.skriptie -public interface InterpretationContext : Expression { +public interface InterpretationContext : Expression { - override fun invokeRaw(context: C): Any? = this + override fun invokeRaw(context: ScriptRuntime): Any? = this - public fun interpret(callable: String?, args: List>?): Expression? + public fun interpret(callable: String?, args: List?): Expression? } -internal fun List>.argForNameOrIndex( +internal fun List.argForNameOrIndex( index : Int, vararg name : String, -) : Expression? { +) : Expression? { return argAtOrNull(index) // forEach { op -> @@ -23,9 +23,9 @@ internal fun List>.argForNameOrIndex( // return argAtOrNull(index) } -internal fun List>.argAt( +internal fun List.argAt( index : Int, -) : Expression { +) : Expression { return get(index) // .let { @@ -35,9 +35,9 @@ internal fun List>.argAt( // } } -internal fun List>.argAtOrNull( +internal fun List.argAtOrNull( index : Int, -) : Expression? { +) : Expression? { return getOrNull(index) // /**/.let { diff --git a/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/LangContext.kt b/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/LangContext.kt index 5e130aa9..fd5b8bd9 100644 --- a/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/LangContext.kt +++ b/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/LangContext.kt @@ -16,6 +16,8 @@ public interface LangContext { public fun neg(a : Any?) : Any? public fun pos(a : Any?) : Any? + public fun toNumber(a: Any?, strict : Boolean = false) : Number + public fun fromKotlin(a : Any?) : Any? public fun toKotlin(a : Any?) : Any? } \ No newline at end of file diff --git a/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/Script.kt b/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/Script.kt index 62f7025a..23dbda09 100644 --- a/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/Script.kt +++ b/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/Script.kt @@ -1,8 +1,8 @@ package io.github.alexzhirkevich.skriptie -public fun interface Script { - public operator fun invoke(context: C): Any? +public fun interface Script { + public operator fun invoke(context: ScriptRuntime): Any? } -public fun Expression.asScript(): Script = Script { invoke(it) } +public fun Expression.asScript(): Script = Script { invoke(it) } diff --git a/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/ScriptEngine.kt b/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/ScriptEngine.kt index 11ba48e9..56926117 100644 --- a/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/ScriptEngine.kt +++ b/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/ScriptEngine.kt @@ -1,22 +1,22 @@ package io.github.alexzhirkevich.skriptie -public interface ScriptEngine : ScriptInterpreter { +public interface ScriptEngine : ScriptInterpreter { - public val context : C + public val runtime : ScriptRuntime public fun reset() { - context.reset() + runtime.reset() } } -public fun ScriptEngine.invoke(script: String) : Any? { - return interpret(script).invoke(context) +public fun ScriptEngine.invoke(script: String) : Any? { + return interpret(script).invoke(runtime) } -public fun ScriptEngine( - context: C, - interpreter: ScriptInterpreter -): ScriptEngine = object : ScriptEngine, ScriptInterpreter by interpreter { - override val context: C +public fun ScriptEngine( + context: ScriptRuntime, + interpreter: ScriptInterpreter +): ScriptEngine = object : ScriptEngine, ScriptInterpreter by interpreter { + override val runtime: ScriptRuntime get() = context } \ No newline at end of file diff --git a/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/ScriptInterpreter.kt b/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/ScriptInterpreter.kt index 1f68d553..d5e92fd3 100644 --- a/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/ScriptInterpreter.kt +++ b/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/ScriptInterpreter.kt @@ -1,6 +1,6 @@ package io.github.alexzhirkevich.skriptie -public interface ScriptInterpreter { +public interface ScriptInterpreter { - public fun interpret(script : String) : Script + public fun interpret(script : String) : Script } \ No newline at end of file diff --git a/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/ScriptRuntime.kt b/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/ScriptRuntime.kt index 6c483243..c31e56aa 100644 --- a/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/ScriptRuntime.kt +++ b/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/ScriptRuntime.kt @@ -11,45 +11,46 @@ public enum class VariableType { public interface ScriptRuntime : LangContext { - public fun hasVariable(name: String): Boolean + public operator fun contains(variable: String): Boolean - public fun getVariable(name: String): Any? + public operator fun get(variable: String): Any? - public fun setVariable(name: String, value: Any?, type: VariableType?) + public fun set(variable: String, value: Any?, type: VariableType?) public fun withScope( extraVariables: Map> = emptyMap(), block: (ScriptRuntime) -> Any? ): Any? + public fun reset() } private class BlockScriptContext( private val parent : ScriptRuntime -) : EcmascriptRuntime(), LangContext by parent { +) : DefaultRuntime(), LangContext by parent { - override fun getVariable(name: String): Any? { - return if (name in variables) { - super.getVariable(name) + override fun get(variable: String): Any? { + return if (variable in variables) { + super.get(variable) } else { - parent.getVariable(name) + parent.get(variable) } } - override fun hasVariable(name: String): Boolean { - return super.hasVariable(name) || parent.hasVariable(name) + override fun contains(variable: String): Boolean { + return super.contains(variable) || parent.contains(variable) } - override fun setVariable(name: String, value: Any?, type: VariableType?) { + override fun set(variable: String, value: Any?, type: VariableType?) { when { - type == VariableType.Global -> parent.setVariable(name, value, type) - type != null || name in variables -> super.setVariable(name, value, type) - else -> parent.setVariable(name, value, type) + type == VariableType.Global -> parent.set(variable, value, type) + type != null || variable in variables -> super.set(variable, value, type) + else -> parent.set(variable, value, type) } } } -public abstract class EcmascriptRuntime : ScriptRuntime { +public abstract class DefaultRuntime : ScriptRuntime { protected val variables: MutableMap> = mutableMapOf() @@ -57,25 +58,25 @@ public abstract class EcmascriptRuntime : ScriptRuntime { BlockScriptContext(this) } - override fun hasVariable(name: String): Boolean { - return name in variables + override fun contains(variable: String): Boolean { + return variable in variables } - override fun setVariable(name: String, value: Any?, type: VariableType?) { - if (type == null && name !in variables) { - unresolvedReference(name) + override fun set(variable: String, value: Any?, type: VariableType?) { + if (type == null && variable !in variables) { + unresolvedReference(variable) } - if (type != null && name in variables) { - throw SyntaxError("Identifier '$name' is already declared") + if (type != null && variable in variables) { + throw SyntaxError("Identifier '$variable' is already declared") } - if (type == null && variables[name]?.first == VariableType.Const) { - throw TypeError("Assignment to constant variable ('$name')") + if (type == null && variables[variable]?.first == VariableType.Const) { + throw TypeError("Assignment to constant variable ('$variable')") } - variables[name] = (type ?: variables[name]?.first)!! to value + variables[variable] = (type ?: variables[variable]?.first)!! to value } - override fun getVariable(name: String): Any? { - return variables[name]?.second + override fun get(variable: String): Any? { + return variables[variable]?.second } final override fun withScope( @@ -84,7 +85,7 @@ public abstract class EcmascriptRuntime : ScriptRuntime { ): Any? { child.reset() extraVariables.forEach { (n, v) -> - child.setVariable(n, v.second, v.first) + child.set(n, v.second, v.first) } return block(child) } diff --git a/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/common/OpFunction.kt b/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/common/Function.kt similarity index 68% rename from skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/common/OpFunction.kt rename to skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/common/Function.kt index db680dcc..331b2d53 100644 --- a/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/common/OpFunction.kt +++ b/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/common/Function.kt @@ -8,22 +8,26 @@ import io.github.alexzhirkevich.skriptie.ecmascript.ESAny import io.github.alexzhirkevich.skriptie.ecmascript.ESObject import io.github.alexzhirkevich.skriptie.invoke -public class FunctionParam( +public class FunctionParam( public val name : String, public val isVararg : Boolean = false, - public val default : Expression? = null + public val default : Expression? = null ) -internal infix fun String.with(default: Expression?) : FunctionParam { +internal infix fun String.with(default: Expression?) : FunctionParam { return FunctionParam(this, false, default) } -internal class OpFunction( +internal interface Callable { + operator fun invoke(args: List, context: ScriptRuntime) : Any? +} + +internal class Function( val name : String, - private val parameters : List>, - private val body : Expression -) { + private val parameters : List, + private val body : Expression +) : Callable { init { val varargs = parameters.count { it.isVararg } @@ -32,9 +36,9 @@ internal class OpFunction( } } - fun invoke( - args: List>, - context: C, + override fun invoke( + args: List, + context: ScriptRuntime, ): Any? { try { val arguments = buildMap { @@ -52,30 +56,28 @@ internal class OpFunction( ) } } - return context.withScope(arguments) { - body.invoke(it as C) - } + return context.withScope(arguments, body::invoke) } catch (ret: BlockReturn) { return ret.value } } } -internal fun OpFunctionExec( +internal fun OpFunctionExec( name : String, - receiver : Expression?, - parameters : List>, -) = Expression { ctx -> + receiver : Expression?, + parameters : List, +) = Expression { ctx -> val function = when (val res = receiver?.invoke(ctx)) { - null -> ctx.getVariable(name) - is ESObject<*> -> res[name] - is ESAny<*> -> { - res as ESAny + null -> ctx.get(name) + is Callable -> res + is ESObject -> res[name] + is ESAny -> { return@Expression res.invoke(name, ctx, parameters) } else -> null - } as? OpFunction ?: unresolvedReference(name) + } as Callable? ?: unresolvedReference(name) function.invoke( args = parameters, diff --git a/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/common/OpAssign.kt b/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/common/OpAssign.kt index da81c7ef..d1cc3b4b 100644 --- a/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/common/OpAssign.kt +++ b/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/common/OpAssign.kt @@ -5,17 +5,17 @@ import io.github.alexzhirkevich.skriptie.ScriptRuntime import io.github.alexzhirkevich.skriptie.VariableType import io.github.alexzhirkevich.skriptie.invoke -internal class OpAssign( +internal class OpAssign( val type : VariableType? = null, val variableName : String, - val assignableValue : Expression, + val assignableValue : Expression, private val merge : ((Any?, Any?) -> Any?)? -) : Expression { +) : Expression { - override fun invokeRaw(context: C): Any? { + override fun invokeRaw(context: ScriptRuntime): Any? { val v = assignableValue.invoke(context) - val current = context.getVariable(variableName) + val current = context.get(variableName) check(merge == null || current != null) { "Cant modify $variableName as it is undefined" @@ -25,8 +25,8 @@ internal class OpAssign( merge.invoke(current, v) } else v - context.setVariable( - name = variableName, + context.set( + variable = variableName, value = value, type = type ) diff --git a/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/common/OpAssignByIndex.kt b/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/common/OpAssignByIndex.kt index e64bedf2..6374ecd7 100644 --- a/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/common/OpAssignByIndex.kt +++ b/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/common/OpAssignByIndex.kt @@ -5,33 +5,34 @@ import io.github.alexzhirkevich.skriptie.ScriptRuntime import io.github.alexzhirkevich.skriptie.VariableType import io.github.alexzhirkevich.skriptie.invoke import io.github.alexzhirkevich.skriptie.javascript.JsArray -import io.github.alexzhirkevich.skriptie.javascript.numberOrNull -internal class OpAssignByIndex( +internal class OpAssignByIndex( private val variableName : String, private val scope : VariableType?, - private val index : Expression, - private val assignableValue : Expression, + private val index : Expression, + private val assignableValue : Expression, private val merge : ((Any?, Any?) -> Any?)? -) : Expression { +) : Expression { - override tailrec fun invokeRaw(context: C): Any? { + override tailrec fun invokeRaw(context: ScriptRuntime): Any? { val v = assignableValue.invoke(context) - val current = context.getVariable(variableName) + val current = context.get(variableName) check(merge == null || current != null) { "Cant modify $variableName as it is undefined" } if (current == null) { - context.setVariable(variableName, mutableListOf(), scope) + context.set(variableName, mutableListOf(), scope) return invoke(context) } else { - val i = index.invoke(context).numberOrNull() + val i = context.toNumber(index.invoke(context)) - val index = checkNotNull(i as? Number) { + check(!i.toDouble().isNaN()) { "Unexpected index: $i" - }.toInt() + } + + val index = i.toInt() return when (current) { diff --git a/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/common/OpBlock.kt b/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/common/OpBlock.kt index c3081b20..36596469 100644 --- a/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/common/OpBlock.kt +++ b/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/common/OpBlock.kt @@ -5,22 +5,20 @@ import io.github.alexzhirkevich.skriptie.ScriptRuntime import io.github.alexzhirkevich.skriptie.invoke -internal class OpBlock( - val expressions: List>, +internal class OpBlock( + val expressions: List, private val scoped : Boolean, -) : Expression { +) : Expression { - override fun invokeRaw(context: C): Any? { + override fun invokeRaw(context: ScriptRuntime): Any? { return if (scoped) { - context.withScope { - invokeInternal(it as C) - } + context.withScope(block = ::invokeInternal) } else { invokeInternal(context) } } - private fun invokeInternal(context: C): Any? { + private fun invokeInternal(context: ScriptRuntime): Any? { if (expressions.isEmpty()) { return Unit } @@ -34,7 +32,7 @@ internal class OpBlock( return invoke(expressions.last(), context) } - private fun invoke(expression: Expression, context: C) : Any? { + private fun invoke(expression: Expression, context: ScriptRuntime) : Any? { val res = expression.invoke(context) return when(expression){ is OpReturn -> throw BlockReturn(res) @@ -51,10 +49,10 @@ internal data object BlockContinue : BlockException() internal data object BlockBreak : BlockException() internal class BlockReturn(val value: Any?) : BlockException() -internal class OpReturn( - val value : Expression -) : Expression by value +internal class OpReturn( + val value : Expression +) : Expression by value -internal class OpContinue : Expression by OpConstant(Unit) -internal class OpBreak : Expression by OpConstant(Unit) +internal class OpContinue : Expression by OpConstant(Unit) +internal class OpBreak : Expression by OpConstant(Unit) diff --git a/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/common/OpBooleans.kt b/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/common/OpBooleans.kt index 26922539..a99e1941 100644 --- a/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/common/OpBooleans.kt +++ b/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/common/OpBooleans.kt @@ -1,22 +1,21 @@ package io.github.alexzhirkevich.skriptie.common import io.github.alexzhirkevich.skriptie.Expression -import io.github.alexzhirkevich.skriptie.ScriptRuntime import io.github.alexzhirkevich.skriptie.invoke -internal fun OpNot( - condition : Expression, +internal fun OpNot( + condition : Expression, isFalse : (Any?) -> Boolean, -) = Expression { +) = Expression { isFalse(condition(it)) } -internal fun OpBoolean( - a : Expression, - b : Expression, +internal fun OpBoolean( + a : Expression, + b : Expression, isFalse : (Any?) -> Boolean, op : (Boolean, Boolean) -> Boolean, -) = Expression { +) = Expression { op(!isFalse(a(it)), !(isFalse(b(it)))) } diff --git a/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/common/OpCompare.kt b/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/common/OpCompare.kt index 06e82cb9..0c930178 100644 --- a/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/common/OpCompare.kt +++ b/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/common/OpCompare.kt @@ -1,14 +1,13 @@ package io.github.alexzhirkevich.skriptie.common import io.github.alexzhirkevich.skriptie.Expression -import io.github.alexzhirkevich.skriptie.ScriptRuntime import io.github.alexzhirkevich.skriptie.invoke -internal fun OpCompare( - a : Expression, - b : Expression, +internal fun OpCompare( + a : Expression, + b : Expression, comparator : (Comparable<*>, Comparable<*>) -> Any -) = Expression { +) = Expression { comparator( a(it) as Comparable<*>, b(it) as Comparable<*> diff --git a/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/common/OpEquals.kt b/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/common/OpEquals.kt index ef69fd91..8d1e5a4e 100644 --- a/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/common/OpEquals.kt +++ b/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/common/OpEquals.kt @@ -1,14 +1,13 @@ package io.github.alexzhirkevich.skriptie.common import io.github.alexzhirkevich.skriptie.Expression -import io.github.alexzhirkevich.skriptie.ScriptRuntime import io.github.alexzhirkevich.skriptie.invoke -internal fun OpEquals( - a : Expression, - b : Expression, +internal fun OpEquals( + a : Expression, + b : Expression, isTyped : Boolean -) = Expression { +) = Expression { OpEqualsImpl(a(it), b(it), isTyped) } diff --git a/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/common/OpGetVariable.kt b/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/common/OpGetVariable.kt index e18398f6..ceed03ab 100644 --- a/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/common/OpGetVariable.kt +++ b/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/common/OpGetVariable.kt @@ -6,25 +6,25 @@ import io.github.alexzhirkevich.skriptie.VariableType import io.github.alexzhirkevich.skriptie.ecmascript.ESAny import io.github.alexzhirkevich.skriptie.invoke -internal fun OpConstant(value: Any?) = - Expression { value } +internal fun OpConstant(value: Any?) = + Expression { value } -internal class OpGetVariable( +internal class OpGetVariable( val name : String, - val receiver : Expression?, + val receiver : Expression?, val assignmentType : VariableType? = null -) : Expression { +) : Expression { - override fun invokeRaw(context: C, ): Any? { + override fun invokeRaw(context: ScriptRuntime, ): Any? { return if (assignmentType != null) { - context.setVariable(name, 0f, assignmentType) + context.set(name, 0f, assignmentType) } else { when (val res = receiver?.invoke(context)) { // is JsObject<*> -> if (name in res) res[name] else Unit - is ESAny<*> -> res[name] + is ESAny -> res[name] - else -> if (context.hasVariable(name)) { - context.getVariable(name) + else -> if (context.contains(name)) { + context.get(name) } else { unresolvedReference(name) } diff --git a/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/common/OpIfCondition.kt b/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/common/OpIfCondition.kt index 5e9ce2f0..35421978 100644 --- a/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/common/OpIfCondition.kt +++ b/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/common/OpIfCondition.kt @@ -1,15 +1,14 @@ package io.github.alexzhirkevich.skriptie.common import io.github.alexzhirkevich.skriptie.Expression -import io.github.alexzhirkevich.skriptie.ScriptRuntime import io.github.alexzhirkevich.skriptie.invoke -internal fun OpIfCondition( - condition : Expression = OpConstant(true), - onTrue : Expression? = null, - onFalse : Expression? = null -) = Expression { +internal fun OpIfCondition( + condition : Expression = OpConstant(true), + onTrue : Expression? = null, + onFalse : Expression? = null +) = Expression { val expr = if (condition(it) as Boolean){ onTrue } else { diff --git a/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/common/OpIncDec.kt b/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/common/OpIncDec.kt index cd5ae4bc..4e52bc95 100644 --- a/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/common/OpIncDec.kt +++ b/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/common/OpIncDec.kt @@ -1,16 +1,15 @@ package io.github.alexzhirkevich.skriptie.common import io.github.alexzhirkevich.skriptie.Expression -import io.github.alexzhirkevich.skriptie.ScriptRuntime import io.github.alexzhirkevich.skriptie.invoke -internal fun OpIncDecAssign( - variable: Expression, +internal fun OpIncDecAssign( + variable: Expression, preAssign : Boolean, op: (Any?) -> Any? -) : Expression { +) : Expression { - val value = Expression { op(variable(it)) } + val value = Expression { op(variable(it)) } val assignment = when { variable is OpGetVariable && variable.assignmentType == null -> OpAssign( diff --git a/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/common/OpIndex.kt b/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/common/OpIndex.kt index bd5d6725..c7d06fb9 100644 --- a/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/common/OpIndex.kt +++ b/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/common/OpIndex.kt @@ -5,21 +5,21 @@ import io.github.alexzhirkevich.skriptie.ScriptRuntime import io.github.alexzhirkevich.skriptie.invoke import io.github.alexzhirkevich.skriptie.javascript.number -internal class OpIndex( - val variable : Expression, - val index : Expression, -) : Expression { +internal class OpIndex( + val variable : Expression, + val index : Expression, +) : Expression { - override fun invokeRaw(context: C): Any { + override fun invokeRaw(context: ScriptRuntime): Any { return invoke(context, variable, index) } companion object { - fun invoke(context: C, variable : Expression, index : Expression) : Any{ + fun invoke(context: ScriptRuntime, variable : Expression, index : Expression) : Any{ val v = checkNotEmpty(variable(context)) val idx = (index(context).number()).toInt() - return v.tryGet(idx) + return v.valueAtIndexOrUnit(idx) } } } \ No newline at end of file diff --git a/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/common/OpLoops.kt b/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/common/OpLoops.kt index 52a5b6e2..1fc63be1 100644 --- a/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/common/OpLoops.kt +++ b/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/common/OpLoops.kt @@ -6,23 +6,23 @@ import io.github.alexzhirkevich.skriptie.VariableType import io.github.alexzhirkevich.skriptie.invoke -internal class OpForLoop( - private val assignment : OpAssign?, - private val increment: Expression?, - private val comparison : Expression?, +internal class OpForLoop( + private val assignment : OpAssign?, + private val increment: Expression?, + private val comparison : Expression?, private val isFalse : (Any?) -> Boolean, - private val body: Expression -) : Expression { + private val body: Expression +) : Expression { - private val condition: (C) -> Boolean = if (comparison == null) { + private val condition: (ScriptRuntime) -> Boolean = if (comparison == null) { { true } } else { { !isFalse(comparison.invoke(it)) } } override fun invokeRaw( - context: C + context: ScriptRuntime ): Any { if (assignment?.type == VariableType.Local || assignment?.type == VariableType.Const) { @@ -36,15 +36,15 @@ internal class OpForLoop( ) ) ), - ) { block(it as C) } + ) { block(it) } } else { assignment?.invoke(context) - context.withScope { block(it as C) } + context.withScope { block(it) } } return Unit } - private fun block(ctx: C) { + private fun block(ctx: ScriptRuntime) { while (condition(ctx)) { try { body(ctx) @@ -60,11 +60,11 @@ internal class OpForLoop( } -internal fun OpDoWhileLoop( - condition : Expression, - body : OpBlock, +internal fun OpDoWhileLoop( + condition : Expression, + body : OpBlock, isFalse : (Any?) -> Boolean -) = Expression { +) = Expression { do { try { body.invoke(it) @@ -77,11 +77,11 @@ internal fun OpDoWhileLoop( } -internal fun OpWhileLoop( - condition : Expression, - body : Expression, +internal fun OpWhileLoop( + condition : Expression, + body : Expression, isFalse : (Any?) -> Boolean -) = Expression { +) = Expression { while (!isFalse(condition.invoke(it))) { try { body.invoke(it) diff --git a/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/common/OpMakeArray.kt b/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/common/OpMakeArray.kt index 44db65da..f251f5ca 100644 --- a/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/common/OpMakeArray.kt +++ b/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/common/OpMakeArray.kt @@ -1,11 +1,10 @@ package io.github.alexzhirkevich.skriptie.common import io.github.alexzhirkevich.skriptie.Expression -import io.github.alexzhirkevich.skriptie.ScriptRuntime import io.github.alexzhirkevich.skriptie.invoke -internal fun OpMakeArray( - items : List> -) = Expression { context -> +internal fun OpMakeArray( + items : List +) = Expression { context -> items.fastMap { it(context) }.toMutableList() } \ No newline at end of file diff --git a/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/common/OpTryCatch.kt b/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/common/OpTryCatch.kt index 1e1b2249..12d85e39 100644 --- a/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/common/OpTryCatch.kt +++ b/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/common/OpTryCatch.kt @@ -1,15 +1,14 @@ package io.github.alexzhirkevich.skriptie.common import io.github.alexzhirkevich.skriptie.Expression -import io.github.alexzhirkevich.skriptie.ScriptRuntime import io.github.alexzhirkevich.skriptie.VariableType import io.github.alexzhirkevich.skriptie.invoke -internal fun OpTryCatch( - tryBlock : Expression, +internal fun OpTryCatch( + tryBlock : Expression, catchVariableName : String?, - catchBlock : Expression?, - finallyBlock : Expression?, + catchBlock : Expression?, + finallyBlock : Expression?, ) = when { catchBlock != null && finallyBlock != null -> TryCatchFinally(tryBlock, catchVariableName, catchBlock, finallyBlock) @@ -19,19 +18,20 @@ internal fun OpTryCatch( else -> error("SyntaxError: Missing catch or finally after try") } -private fun TryCatchFinally( - tryBlock : Expression, +private fun TryCatchFinally( + tryBlock : Expression, catchVariableName : String?, - catchBlock : Expression, - finallyBlock : Expression, -) = Expression { + catchBlock : Expression, + finallyBlock : Expression, +) = Expression { try { tryBlock(it) } catch (t: Throwable) { if (catchVariableName != null) { - it.withScope(mapOf(catchVariableName to (VariableType.Const to t))) { - catchBlock(it as C) - } + it.withScope( + extraVariables = mapOf(catchVariableName to (VariableType.Const to t)), + block = catchBlock::invoke + ) } else { catchBlock(it) } @@ -40,18 +40,19 @@ private fun TryCatchFinally( } } -private fun TryCatch( - tryBlock : Expression, +private fun TryCatch( + tryBlock : Expression, catchVariableName : String?, - catchBlock : Expression -) = Expression { + catchBlock : Expression +) = Expression { try { tryBlock(it) } catch (t: Throwable) { if (catchVariableName != null) { - it.withScope(mapOf(catchVariableName to (VariableType.Const to t))) { - catchBlock(it as C) - } + it.withScope( + extraVariables = mapOf(catchVariableName to (VariableType.Const to t)), + block = catchBlock::invoke + ) } else { catchBlock(it) } @@ -59,10 +60,10 @@ private fun TryCatch( } -private fun TryFinally( - tryBlock : Expression, - finallyBlock : Expression, -) = Expression { +private fun TryFinally( + tryBlock : Expression, + finallyBlock : Expression, +) = Expression { try { tryBlock(it) } finally { diff --git a/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/common/ScriptUtil.kt b/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/common/ScriptUtil.kt index d3c39784..c36e8cc6 100644 --- a/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/common/ScriptUtil.kt +++ b/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/common/ScriptUtil.kt @@ -6,13 +6,13 @@ import io.github.alexzhirkevich.skriptie.invoke import kotlin.contracts.ExperimentalContracts import kotlin.contracts.contract -internal fun Delegate( - a : Expression, - b : Expression, +internal fun Delegate( + a : Expression, + b : Expression, op : (Any?, Any?) -> Any? -) = Expression { op(a(it), b(it)) } +) = Expression { op(a(it), b(it)) } -internal fun Delegate(a : Expression, op : (Any?) -> Any?) = Expression { +internal fun Delegate(a : Expression, op : (Any?) -> Any?) = Expression { op(a(it)) } @@ -25,22 +25,22 @@ internal fun checkNotEmpty(value : T?) : T { } -internal fun Expression.cast(block: (T) -> R) : Expression = +internal fun Expression.cast(block: (T) -> R) : Expression = Expression { block(invoke(it) as T) } -internal fun Expression.withCast(block: T.( - context: C, -) -> R) : Expression = Expression { +internal fun Expression.withCast(block: T.( + context: ScriptRuntime, +) -> R) : Expression = Expression { block(invoke(it) as T, it) } -internal operator fun Any.get(index : Int) : Any { - return checkNotNull(tryGet(index)){ +internal fun Any.valueAtIndex(index : Int) : Any { + return checkNotNull(valueAtIndexOrUnit(index)){ "Index $index out of bounds of $this length" } } -internal fun Any.tryGet(index : Int) : Any { +internal fun Any.valueAtIndexOrUnit(index : Int) : Any { return when (this) { is Map<*, *> -> { (this as Map) diff --git a/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/ecmascript/ESAny.kt b/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/ecmascript/ESAny.kt index f37d4447..86764f0a 100644 --- a/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/ecmascript/ESAny.kt +++ b/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/ecmascript/ESAny.kt @@ -3,13 +3,13 @@ package io.github.alexzhirkevich.skriptie.ecmascript import io.github.alexzhirkevich.skriptie.Expression import io.github.alexzhirkevich.skriptie.ScriptRuntime -public interface ESAny { +public interface ESAny { public val type : String get() = "object" public operator fun get(property: String): Any? - public operator fun invoke(function: String, context: C, arguments: List>): Any? { + public operator fun invoke(function: String, context: ScriptRuntime, arguments: List): Any? { return when { function == "toString" && arguments.isEmpty() -> toString() else -> null diff --git a/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/ecmascript/ESFunction.kt b/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/ecmascript/ESFunction.kt new file mode 100644 index 00000000..1a62051f --- /dev/null +++ b/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/ecmascript/ESFunction.kt @@ -0,0 +1,19 @@ +package io.github.alexzhirkevich.skriptie.ecmascript + +import io.github.alexzhirkevich.skriptie.common.Callable + +internal interface ESFunction : ESObject, Callable { + override val type: String + get() = "function" +} + +internal abstract class ESFunctionBase( + name : String +) : ESObjectBase(name), ESFunction { + override val type: String + get() = "function" + + override fun toString(): String { + return "function $name() { [native code] }" + } +} \ No newline at end of file diff --git a/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/ecmascript/ESNumber.kt b/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/ecmascript/ESNumber.kt new file mode 100644 index 00000000..2e44a876 --- /dev/null +++ b/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/ecmascript/ESNumber.kt @@ -0,0 +1,37 @@ +package io.github.alexzhirkevich.skriptie.ecmascript + +import io.github.alexzhirkevich.skriptie.Expression +import io.github.alexzhirkevich.skriptie.ScriptRuntime +import io.github.alexzhirkevich.skriptie.invoke +import io.github.alexzhirkevich.skriptie.javascript.numberOrNull + +internal class ESNumber : ESFunctionBase("Number") { + + init { + setFunction( + "isFinite".func("number") { + val num = it.getOrNull(0)?.numberOrNull() ?: return@func false + num.toDouble().isFinite() + } + ) + } + + override fun get(property: String): Any? { + return when (property) { + "EPSILON" -> Double.MIN_VALUE + "length" -> Double.MIN_VALUE + "MAX_SAFE_INTEGER" -> Long.MAX_VALUE + "MAX_VALUE" -> Double.MAX_VALUE + "MIN_SAFE_INTEGER" -> Long.MIN_VALUE + "NaN" -> Double.NaN + "NEGATIVE_INFINITY" -> Double.NEGATIVE_INFINITY + "POSITIVE_INFINITY" -> Double.POSITIVE_INFINITY + else -> super.get(property) + } + } + + override fun invoke(args: List, context: ScriptRuntime): Any? { + return context.toNumber(args.single().invoke(context)) + } +} + diff --git a/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/ecmascript/ESObject.kt b/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/ecmascript/ESObject.kt index 261de79e..48fe36b5 100644 --- a/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/ecmascript/ESObject.kt +++ b/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/ecmascript/ESObject.kt @@ -2,34 +2,33 @@ package io.github.alexzhirkevich.skriptie.ecmascript import io.github.alexzhirkevich.skriptie.Expression import io.github.alexzhirkevich.skriptie.ScriptRuntime +import io.github.alexzhirkevich.skriptie.common.Callable +import io.github.alexzhirkevich.skriptie.common.Function import io.github.alexzhirkevich.skriptie.common.FunctionParam -import io.github.alexzhirkevich.skriptie.common.OpFunction import io.github.alexzhirkevich.skriptie.common.OpGetVariable import io.github.alexzhirkevich.skriptie.common.unresolvedReference -public interface ESObject : ESAny { +public interface ESObject : ESAny { public operator fun set(property: String, value: Any?) public operator fun contains(property: String): Boolean - override fun invoke(function: String, context: C, arguments: List>): Any? { + override fun invoke(function: String, context: ScriptRuntime, arguments: List): Any? { val f = get(function) - if (f !is OpFunction<*>) { + if (f !is Callable) { unresolvedReference(function) } - - f as OpFunction return f.invoke(arguments, context) } } -public class ObjectImpl( - private val name : String, +internal open class ESObjectBase( + internal val name : String, private val map : MutableMap = mutableMapOf() -) : ESObject { +) : ESObject { init { if ("toString" !in map){ - map["toString"] = OpFunction( + map["toString"] = Function( name = "toString", parameters = emptyList(), body = Expression { toString() }) @@ -55,19 +54,19 @@ public class ObjectImpl( } -public sealed interface ObjectScope { +public sealed interface ObjectScope { public infix fun String.eq(value: Any?) public fun String.func( - vararg args: FunctionParam, - body: (args: List>) -> Expression + vararg args: FunctionParam, + body: (args: List) -> Expression ) public fun String.func( vararg args: String, - params: (String) -> FunctionParam = { FunctionParam(it) }, - body: (args: List>) -> Expression + params: (String) -> FunctionParam = { FunctionParam(it) }, + body: (args: List) -> Expression ) { func( args = args.map(params).toTypedArray(), @@ -76,14 +75,14 @@ public sealed interface ObjectScope { } } -private class ObjectScopeImpl(name: String) : ObjectScope { - val o = ObjectImpl(name) +private class ObjectScopeImpl(name: String) : ObjectScope { + val o = ESObjectBase(name) override fun String.func( - vararg args: FunctionParam, - body: (args: List>) -> Expression + vararg args: FunctionParam, + body: (args: List) -> Expression ) { - this eq OpFunction( + this eq Function( this, parameters = args.toList(), body = body(args.map { OpGetVariable(it.name, null) }) @@ -95,6 +94,32 @@ private class ObjectScopeImpl(name: String) : ObjectScope } } -public fun Object(name: String, builder : ObjectScope.() -> Unit) : ESObject { - return ObjectScopeImpl(name).also(builder).o -} \ No newline at end of file + +public fun Object(name: String, builder : ObjectScope.() -> Unit) : ESObject { + return ObjectScopeImpl(name).also(builder).o +} + +internal fun ESObject.setFunction(function: Function) = set(function.name, function) + +internal fun String.func( + vararg args: FunctionParam, + body: ScriptRuntime.(args: List) -> Any? +) = Function( + this, + parameters = args.toList(), + body = { + with(it) { + body(args.map { get(it.name) }) + } + } +) + + +internal fun String.func( + vararg args: String, + params: (String) -> FunctionParam = { FunctionParam(it) }, + body: ScriptRuntime.(args: List) -> Any? +) : Function = func( + args = args.map(params).toTypedArray(), + body = body +) \ No newline at end of file diff --git a/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/ecmascript/ESRuntime.kt b/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/ecmascript/ESRuntime.kt new file mode 100644 index 00000000..1fea9c17 --- /dev/null +++ b/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/ecmascript/ESRuntime.kt @@ -0,0 +1,21 @@ +package io.github.alexzhirkevich.skriptie.ecmascript + +import io.github.alexzhirkevich.skriptie.DefaultRuntime +import io.github.alexzhirkevich.skriptie.VariableType + +public abstract class ESRuntime() : DefaultRuntime() { + + init { + init() + } + + override fun reset() { + super.reset() + init() + } + + private fun init(){ + set("Number", ESNumber(), VariableType.Const) + set("Infinity", Double.POSITIVE_INFINITY, VariableType.Const) + } +} \ No newline at end of file diff --git a/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/ecmascript/EcmascriptInterpretationContext.kt b/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/ecmascript/EcmascriptInterpretationContext.kt index 3aab8cec..81524d9d 100644 --- a/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/ecmascript/EcmascriptInterpretationContext.kt +++ b/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/ecmascript/EcmascriptInterpretationContext.kt @@ -1,10 +1,9 @@ package io.github.alexzhirkevich.skriptie.ecmascript import io.github.alexzhirkevich.skriptie.InterpretationContext -import io.github.alexzhirkevich.skriptie.ScriptRuntime -public abstract class EcmascriptInterpretationContext( +public abstract class EcmascriptInterpretationContext( public val namedArgumentsEnabled : Boolean = false -) : InterpretationContext { +) : InterpretationContext { } \ No newline at end of file diff --git a/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/ecmascript/EcmascriptInterpreter.kt b/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/ecmascript/EcmascriptInterpreter.kt index 33cf46d8..81a022d6 100644 --- a/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/ecmascript/EcmascriptInterpreter.kt +++ b/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/ecmascript/EcmascriptInterpreter.kt @@ -3,14 +3,13 @@ package io.github.alexzhirkevich.skriptie.ecmascript import io.github.alexzhirkevich.skriptie.InterpretationContext import io.github.alexzhirkevich.skriptie.LangContext import io.github.alexzhirkevich.skriptie.Script -import io.github.alexzhirkevich.skriptie.ScriptRuntime import io.github.alexzhirkevich.skriptie.ScriptInterpreter -public class EcmascriptInterpreter( - private val interpretationContext : InterpretationContext, +public class EcmascriptInterpreter( + private val interpretationContext : InterpretationContext, private val langContext: LangContext -) : ScriptInterpreter { - override fun interpret(script: String): Script { +) : ScriptInterpreter { + override fun interpret(script: String): Script { return EcmascriptInterpreterImpl(script, langContext, interpretationContext).interpret() } } diff --git a/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/ecmascript/EcmascriptInterpreterImpl.kt b/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/ecmascript/EcmascriptInterpreterImpl.kt index 12928dd1..81e6757c 100644 --- a/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/ecmascript/EcmascriptInterpreterImpl.kt +++ b/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/ecmascript/EcmascriptInterpreterImpl.kt @@ -7,6 +7,7 @@ import io.github.alexzhirkevich.skriptie.Script import io.github.alexzhirkevich.skriptie.ScriptRuntime import io.github.alexzhirkevich.skriptie.VariableType import io.github.alexzhirkevich.skriptie.common.Delegate +import io.github.alexzhirkevich.skriptie.common.Function import io.github.alexzhirkevich.skriptie.common.FunctionParam import io.github.alexzhirkevich.skriptie.common.OpAssign import io.github.alexzhirkevich.skriptie.common.OpAssignByIndex @@ -20,7 +21,6 @@ import io.github.alexzhirkevich.skriptie.common.OpDoWhileLoop import io.github.alexzhirkevich.skriptie.common.OpEquals import io.github.alexzhirkevich.skriptie.common.OpEqualsComparator import io.github.alexzhirkevich.skriptie.common.OpForLoop -import io.github.alexzhirkevich.skriptie.common.OpFunction import io.github.alexzhirkevich.skriptie.common.OpFunctionExec import io.github.alexzhirkevich.skriptie.common.OpGetVariable import io.github.alexzhirkevich.skriptie.common.OpGreaterComparator @@ -40,7 +40,7 @@ import io.github.alexzhirkevich.skriptie.isAssignable import kotlin.contracts.ExperimentalContracts import kotlin.contracts.contract -internal val EXPR_DEBUG_PRINT_ENABLED = true +internal val EXPR_DEBUG_PRINT_ENABLED = false internal enum class LogicalContext { And, Or, Compare @@ -51,17 +51,17 @@ internal enum class BlockContext { } -internal class EcmascriptInterpreterImpl( +internal class EcmascriptInterpreterImpl( private val expr : String, private val langContext: LangContext, - private val globalContext : InterpretationContext, + private val globalContext : InterpretationContext, ) { private var pos = -1 private var ch: Char = ' ' - private var blockFunctionRegistrar = mutableListOf>() + private var blockFunctionRegistrar = mutableListOf() - fun interpret(): Script { + fun interpret(): Script { val expressions = buildList { pos = -1 ch = ' ' @@ -184,7 +184,7 @@ internal class EcmascriptInterpreterImpl( } } - private fun parseAssignment(context: Expression, blockContext : List): Expression { + private fun parseAssignment(context: Expression, blockContext : List): Expression { var x = parseExpressionOp(context, blockContext = blockContext) if (EXPR_DEBUG_PRINT_ENABLED) { println("Parsing assignment for $x") @@ -225,7 +225,7 @@ internal class EcmascriptInterpreterImpl( } } - private fun parseAssignmentValue(x: Expression, merge: ((Any?, Any?) -> Any?)? = null) = + private fun parseAssignmentValue(x: Expression, merge: ((Any?, Any?) -> Any?)? = null) = when { x is OpIndex && x.variable is OpGetVariable -> OpAssignByIndex( variableName = x.variable.name, @@ -254,10 +254,10 @@ internal class EcmascriptInterpreterImpl( } private fun parseExpressionOp( - context: Expression, + context: Expression, logicalContext: LogicalContext? = null, blockContext : List - ): Expression { + ): Expression { var x = parseTermOp(context, blockContext) while (true) { prepareNextChar() @@ -343,7 +343,7 @@ internal class EcmascriptInterpreterImpl( } } - private fun parseTermOp(context: Expression, blockContext : List): Expression { + private fun parseTermOp(context: Expression, blockContext : List): Expression { var x = parseFactorOp(context, blockContext) while (true) { prepareNextChar() @@ -371,7 +371,7 @@ internal class EcmascriptInterpreterImpl( } } - private fun parseFactorOp(context: Expression, blockContext : List): Expression { + private fun parseFactorOp(context: Expression, blockContext : List): Expression { val parsedOp = when { nextCharIs('{'::equals) -> parseBlock(context = emptyList()) @@ -557,10 +557,10 @@ internal class EcmascriptInterpreterImpl( return parsedOp.finish(blockContext) } - private fun Expression.finish(blockContext : List): Expression { + private fun Expression.finish(blockContext : List): Expression { return when { // inplace function invocation - this is InterpretationContext<*> && nextCharIs { it == '(' } -> { + this is InterpretationContext && nextCharIs { it == '(' } -> { parseFunction(this, null, blockContext) } // begin condition || property || index @@ -572,7 +572,7 @@ internal class EcmascriptInterpreterImpl( } } - private fun parseFunctionArgs(name: String?): List>? { + private fun parseFunctionArgs(name: String?): List? { if (!nextCharIs('('::equals)) { return null @@ -596,10 +596,10 @@ internal class EcmascriptInterpreterImpl( } private fun parseFunction( - context: Expression, + context: Expression, func: String?, blockContext : List - ): Expression { + ): Expression { return when (func) { "var", "let", "const" -> { @@ -666,7 +666,7 @@ internal class EcmascriptInterpreterImpl( val body = parseBlock(context = blockContext + BlockContext.Loop) - check(body is OpBlock) { + check(body is OpBlock) { "Invalid do/while syntax" } @@ -753,7 +753,7 @@ internal class EcmascriptInterpreterImpl( } return when (context) { - is InterpretationContext -> context.interpret(func, args) + is InterpretationContext -> context.interpret(func, args) ?: run { if (args != null && func != null) { if (EXPR_DEBUG_PRINT_ENABLED) { @@ -805,7 +805,7 @@ internal class EcmascriptInterpreterImpl( } } - private fun parseWhileCondition(): Expression { + private fun parseWhileCondition(): Expression { check(eat('(')) { "Missing while loop condition" } @@ -818,7 +818,7 @@ internal class EcmascriptInterpreterImpl( return condition } - private fun parseTryCatch(blockContext : List) : Expression { + private fun parseTryCatch(blockContext : List) : Expression { val tryBlock = parseBlock(requireBlock = true, context = blockContext) val catchBlock = if (eatSequence("catch")) { @@ -849,7 +849,7 @@ internal class EcmascriptInterpreterImpl( ) } - private fun parseForLoop(parentBlockContext: List): Expression { + private fun parseForLoop(parentBlockContext: List): Expression { check(eat('(')) val assign = if (eat(';')) null else parseAssignment(globalContext, emptyList()) @@ -881,7 +881,7 @@ internal class EcmascriptInterpreterImpl( ) } - private fun parseFunctionDefinition(name: String? = null, blockContext: List): Expression { + private fun parseFunctionDefinition(name: String? = null, blockContext: List): Expression { val start = pos @@ -900,8 +900,8 @@ internal class EcmascriptInterpreterImpl( val args = parseFunctionArgs(actualName).let { args -> args?.map { when (it) { - is OpGetVariable -> FunctionParam(name = it.name, default = null) - is OpAssign -> FunctionParam( + is OpGetVariable -> FunctionParam(name = it.name, default = null) + is OpAssign -> FunctionParam( name = it.variableName, default = it.assignableValue ) @@ -926,7 +926,7 @@ internal class EcmascriptInterpreterImpl( context = blockContext + BlockContext.Function ) - val function = OpFunction( + val function = Function( name = actualName, parameters = args, body = block @@ -944,7 +944,7 @@ internal class EcmascriptInterpreterImpl( scoped: Boolean = true, requireBlock: Boolean = false, context: List - ): Expression { + ): Expression { val oldRegistrar = blockFunctionRegistrar try { diff --git a/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/javascript/JSGlobalContext.kt b/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/javascript/JSGlobalContext.kt index f10575e2..b7e929f1 100644 --- a/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/javascript/JSGlobalContext.kt +++ b/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/javascript/JSGlobalContext.kt @@ -9,12 +9,12 @@ import io.github.alexzhirkevich.skriptie.invoke public open class JSGlobalContext( namedArgumentsEnabled : Boolean = false -) : EcmascriptInterpretationContext(namedArgumentsEnabled) { +) : EcmascriptInterpretationContext(namedArgumentsEnabled) { override fun interpret( callable: String?, - args: List>? - ): Expression? { + args: List? + ): Expression? { return if (args != null){ when (callable) { "typeof" -> { @@ -29,12 +29,12 @@ public open class JSGlobalContext( -private fun JsTypeof(value : Expression) = Expression { +private fun JsTypeof(value : Expression) = Expression { value(it).let { when (it) { null -> "object" Unit -> "undefined" - is ESAny<*> -> it.type + is ESAny -> it.type else -> it::class.simpleName } } diff --git a/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/javascript/JSRuntime.kt b/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/javascript/JSRuntime.kt index 579a7cc3..9716735d 100644 --- a/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/javascript/JSRuntime.kt +++ b/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/javascript/JSRuntime.kt @@ -1,11 +1,11 @@ package io.github.alexzhirkevich.skriptie.javascript -import io.github.alexzhirkevich.skriptie.EcmascriptRuntime import io.github.alexzhirkevich.skriptie.VariableType import io.github.alexzhirkevich.skriptie.common.fastMap +import io.github.alexzhirkevich.skriptie.ecmascript.ESRuntime import kotlin.math.absoluteValue -public open class JSRuntime : EcmascriptRuntime() { +public open class JSRuntime : ESRuntime() { init { recreate() @@ -26,44 +26,46 @@ public open class JSRuntime : EcmascriptRuntime() { } override fun sum(a: Any?, b: Any?): Any? { - return fromKotlin( - jssum( - fromKotlin(a)?.numberOrThis(false), - fromKotlin(b)?.numberOrThis(false) - ) + return jssum( + a?.numberOrThis(false), + b?.numberOrThis(false) ) } override fun sub(a: Any?, b: Any?): Any? { - return fromKotlin(jssub(fromKotlin(a)?.numberOrThis(), fromKotlin(b)?.numberOrThis())) + return jssub(a?.numberOrThis(), b?.numberOrThis()) } override fun mul(a: Any?, b: Any?): Any? { - return fromKotlin(jsmul(fromKotlin(a)?.numberOrThis(), fromKotlin(b)?.numberOrThis())) + return jsmul(a?.numberOrThis(), b?.numberOrThis()) } override fun div(a: Any?, b: Any?): Any? { - return fromKotlin(jsdiv(fromKotlin(a)?.numberOrThis(), fromKotlin(b)?.numberOrThis())) + return jsdiv(a?.numberOrThis(), b?.numberOrThis()) } override fun mod(a: Any?, b: Any?): Any? { - return fromKotlin(jsmod(fromKotlin(a)?.numberOrThis(), fromKotlin(b)?.numberOrThis())) + return jsmod(a?.numberOrThis(), b?.numberOrThis()) } override fun inc(a: Any?): Any? { - return fromKotlin(jsinc(fromKotlin(a)?.numberOrThis())) + return jsinc(a?.numberOrThis()) } override fun dec(a: Any?): Any? { - return fromKotlin(jsdec(fromKotlin(a)?.numberOrThis())) + return jsdec(a?.numberOrThis()) } override fun neg(a: Any?): Any? { - return fromKotlin(jsneg(fromKotlin(a)?.numberOrThis())) + return jsneg(a?.numberOrThis()) } override fun pos(a: Any?): Any? { - return fromKotlin(jspos(fromKotlin(a)?.numberOrThis())) + return jspos(a?.numberOrThis()) + } + + override fun toNumber(a: Any?, strict : Boolean): Number { + return a.numberOrNull(withNaNs = !strict) ?: Double.NaN } override fun fromKotlin(a: Any?): Any? { @@ -89,8 +91,7 @@ public open class JSRuntime : EcmascriptRuntime() { } private fun recreate(){ - variables["Math"] = VariableType.Const to JsMath() - variables["Infinity"] = VariableType.Const to Double.POSITIVE_INFINITY + set("Math", JsMath(), VariableType.Const) } } @@ -256,4 +257,4 @@ internal fun Any?.number(withNaNs : Boolean = true) : Number = checkNotNull(numb "Expected number but got $this" } -internal fun Any.numberOrThis(withMagic : Boolean = true) : Any = numberOrNull(withMagic) ?: this +internal fun Any?.numberOrThis(withMagic : Boolean = true) : Any? = numberOrNull(withMagic) ?: this diff --git a/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/javascript/JsArray.kt b/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/javascript/JsArray.kt index c082213c..8af360db 100644 --- a/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/javascript/JsArray.kt +++ b/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/javascript/JsArray.kt @@ -1,6 +1,7 @@ package io.github.alexzhirkevich.skriptie.javascript import io.github.alexzhirkevich.skriptie.Expression +import io.github.alexzhirkevich.skriptie.ScriptRuntime import io.github.alexzhirkevich.skriptie.argAt import io.github.alexzhirkevich.skriptie.common.checkNotEmpty import io.github.alexzhirkevich.skriptie.ecmascript.ESAny @@ -11,7 +12,7 @@ import kotlin.jvm.JvmInline @JvmInline internal value class JsArray( override val value : MutableList -) : MutableList by value, ESAny, JsWrapper> { +) : MutableList by value, ESAny, JsWrapper> { // override fun compareTo(other: JsArray): Int { // repeat(min(v.size, other.v.size)) { @@ -32,8 +33,8 @@ internal value class JsArray( override fun invoke( function: String, - context: JSRuntime, - arguments: List> + context: ScriptRuntime, + arguments: List ): Any? { return when (function){ "indexOf" -> value.indexOf(false,function, context, arguments) @@ -46,8 +47,8 @@ internal value class JsArray( private fun List<*>.indexOf( last : Boolean, function: String, - context: JSRuntime, - arguments: List> + context: ScriptRuntime, + arguments: List ): Any { checkArgs(arguments, 1, function) val search = checkNotEmpty(arguments.argAt(0).invoke(context)) diff --git a/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/javascript/JsMath.kt b/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/javascript/JsMath.kt index 2f8198b1..32ccb678 100644 --- a/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/javascript/JsMath.kt +++ b/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/javascript/JsMath.kt @@ -1,7 +1,6 @@ package io.github.alexzhirkevich.skriptie.javascript import io.github.alexzhirkevich.skriptie.Expression -import io.github.alexzhirkevich.skriptie.ScriptRuntime import io.github.alexzhirkevich.skriptie.argAt import io.github.alexzhirkevich.skriptie.common.FunctionParam import io.github.alexzhirkevich.skriptie.common.fastMap @@ -13,9 +12,9 @@ import io.github.alexzhirkevich.skriptie.invoke import kotlin.math.* import kotlin.random.Random -internal fun JsMath() : ESObject { +internal fun JsMath() : ESObject { - return Object("Math") { + return Object("Math") { "PI" eq PI "E" eq E @@ -73,46 +72,38 @@ internal fun JsMath() : ESObject { } } -private fun op1( - args: List>, +private fun op1( + args: List, func: (Double) -> Number -): Expression { +): Expression { checkArgs(args, 1, "") - val a = args.argAt(0) + val numExpr = args.argAt(0) return Expression { - var a = a(it)?.numberOrThis() ?: 0.0 + val value = numExpr(it)?.numberOrThis() ?: 0.0 - if (a !is Number){ - a = a.toString().toDoubleOrNull() ?: return@Expression Double.NaN - } - - if (a is Number){ - func(a.toDouble()) + if (value is Number){ + func(value.toDouble()) } else { Double.NaN } } } -private fun op2( - args: List>, +private fun op2( + args: List, func: (Double, Double) -> Number, -): Expression { +): Expression { checkArgs(args, 2, "") - val a = args.argAt(0) - val b = args.argAt(1) + val aExpr = args.argAt(0) + val bExpr = args.argAt(1) + return Expression { - var a = a(it)?.numberOrThis() ?: 0.0 - var b = b(it)?.numberOrThis() ?: 0.0 - if (a !is Number){ - a = a.toString().toDoubleOrNull() ?: return@Expression Double.NaN - } - if (b !is Number){ - b = b.toString().toDoubleOrNull() ?: return@Expression Double.NaN - } + val a = aExpr(it)?.numberOrThis() ?: 0.0 + val b = bExpr(it)?.numberOrThis() ?: 0.0 + if (a is Number && b is Number){ func(a.toDouble(), b.toDouble()) } else { @@ -121,16 +112,16 @@ private fun op2( } } -private fun opVararg( - args: List>, +private fun opVararg( + args: List, func: (List) -> Number, -): Expression { +): Expression { check(args.isNotEmpty()){ } return Expression { context -> val a = (args[0].invoke(context) as List<*>).fastMap { - it.numberOrNull(withNaNs = false)?.toDouble() ?: return@Expression Double.NaN + context.toNumber(it, strict = false).toDouble() } func(a) } diff --git a/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/javascript/JsNumber.kt b/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/javascript/JsNumber.kt index a889715d..b74cdc23 100644 --- a/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/javascript/JsNumber.kt +++ b/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/javascript/JsNumber.kt @@ -1,6 +1,7 @@ package io.github.alexzhirkevich.skriptie.javascript import io.github.alexzhirkevich.skriptie.Expression +import io.github.alexzhirkevich.skriptie.ScriptRuntime import io.github.alexzhirkevich.skriptie.argAtOrNull import io.github.alexzhirkevich.skriptie.common.unresolvedReference import io.github.alexzhirkevich.skriptie.ecmascript.ESAny @@ -12,9 +13,9 @@ import kotlin.math.roundToInt import kotlin.math.roundToLong @JvmInline -internal value class JsNumber( +public value class JsNumber( override val value : Number -) : ESAny, JsWrapper, Comparable { +) : ESAny, JsWrapper, Comparable { override val type: String get() = "number" @@ -28,8 +29,8 @@ internal value class JsNumber( override fun invoke( function: String, - context: JSRuntime, - arguments: List> + context: ScriptRuntime, + arguments: List ): Any? { return when(function){ "toFixed" -> { diff --git a/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/javascript/JsString.kt b/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/javascript/JsString.kt index bb0c5237..3e54eb2e 100644 --- a/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/javascript/JsString.kt +++ b/skriptie/src/commonMain/kotlin/io/github/alexzhirkevich/skriptie/javascript/JsString.kt @@ -1,10 +1,11 @@ package io.github.alexzhirkevich.skriptie.javascript import io.github.alexzhirkevich.skriptie.Expression +import io.github.alexzhirkevich.skriptie.ScriptRuntime import io.github.alexzhirkevich.skriptie.argAt import io.github.alexzhirkevich.skriptie.argAtOrNull import io.github.alexzhirkevich.skriptie.common.checkNotEmpty -import io.github.alexzhirkevich.skriptie.common.tryGet +import io.github.alexzhirkevich.skriptie.common.valueAtIndexOrUnit import io.github.alexzhirkevich.skriptie.ecmascript.ESAny import io.github.alexzhirkevich.skriptie.ecmascript.checkArgs import io.github.alexzhirkevich.skriptie.ecmascript.checkArgsNotNull @@ -14,7 +15,7 @@ import kotlin.jvm.JvmInline @JvmInline internal value class JsString( override val value : String -) : ESAny, JsWrapper, Comparable { +) : ESAny, JsWrapper, Comparable { override val type: String get() = "string" @@ -33,8 +34,8 @@ internal value class JsString( override fun invoke( function: String, - context: JSRuntime, - arguments: List> + context: ScriptRuntime, + arguments: List ): Any? { return when (function) { "charAt", "at" -> value.charAt(function, context, arguments) @@ -67,19 +68,19 @@ internal value class JsString( private fun String.charAt( function: String, - context: JSRuntime, - arguments: List> + context: ScriptRuntime, + arguments: List ): Any { checkArgs(arguments, 1, function) val idx = (arguments.argAt(0).invoke(context).number()).toInt() - return tryGet(idx) + return valueAtIndexOrUnit(idx) } private fun String.indexOf( last : Boolean, function: String, - context: JSRuntime, - arguments: List> + context: ScriptRuntime, + arguments: List ): Any { checkArgs(arguments, 1, function) val search = checkNotEmpty(arguments.argAt(0).invoke(context).toString()[0]) @@ -91,8 +92,8 @@ private fun String.indexOf( private fun String.charCodeAt( function: String, - context: JSRuntime, - arguments: List> + context: ScriptRuntime, + arguments: List ): Int { checkArgs(arguments, 1, function) val ind = arguments.argAt(0).invoke(context).number().toInt() @@ -101,8 +102,8 @@ private fun String.charCodeAt( private fun String.endsWith( function: String, - context: JSRuntime, - arguments: List> + context: ScriptRuntime, + arguments: List ): Boolean { checkArgsNotNull(arguments, function) val searchString = arguments.argAt(0).invoke(context).toString() @@ -116,8 +117,8 @@ private fun String.endsWith( private fun String.startsWith( function: String, - context: JSRuntime, - arguments: List> + context: ScriptRuntime, + arguments: List ): Boolean { val searchString = arguments.argAt(0).invoke(context).toString() val position = arguments.argAtOrNull(1)?.number()?.toInt() @@ -131,8 +132,8 @@ private fun String.startsWith( private fun String.includes( function: String, - context: JSRuntime, - arguments: List> + context: ScriptRuntime, + arguments: List ): Boolean { val searchString = arguments.argAt(0).invoke(context).toString() val position = arguments.argAtOrNull(1)?.invoke(context)?.number()?.toInt() @@ -145,8 +146,8 @@ private fun String.includes( } private fun String.padStart( function: String, - context: JSRuntime, - arguments: List> + context: ScriptRuntime, + arguments: List ): String { val targetLength = arguments.argAt(0).invoke(context).number().toInt() val padString = arguments.argAtOrNull(1)?.invoke(context)?.toString() ?: " " @@ -161,8 +162,8 @@ private fun String.padStart( } private fun String.padEnd( function: String, - context: JSRuntime, - arguments: List> + context: ScriptRuntime, + arguments: List ): String { val targetLength = arguments.argAt(0).invoke(context).number().toInt() val padString = arguments.argAtOrNull(1)?.invoke(context)?.toString() ?: " " @@ -177,8 +178,8 @@ private fun String.padEnd( private fun String.match( function: String, - context: JSRuntime, - arguments: List> + context: ScriptRuntime, + arguments: List ): Boolean { checkArgs(arguments, 1, function) val regex = arguments.argAt(0).invoke(context).toString() @@ -188,8 +189,8 @@ private fun String.match( private fun String.replace( all : Boolean, function: String, - context: JSRuntime, - arguments: List> + context: ScriptRuntime, + arguments: List ): String { checkArgs(arguments, 2, function) val pattern = arguments.argAt(0).invoke(context).toString() @@ -204,8 +205,8 @@ private fun String.replace( private fun String.repeat( function: String, - context: JSRuntime, - arguments: List> + context: ScriptRuntime, + arguments: List ): String { checkArgs(arguments, 1, function) val count = arguments.argAt(0).invoke(context).number().toInt() @@ -214,8 +215,8 @@ private fun String.repeat( private fun String.substring( function: String, - context: JSRuntime, - arguments: List> + context: ScriptRuntime, + arguments: List ): String { val start = arguments.get(0).invoke(context).number().toInt() val end = arguments.get(0).invoke(context)?.number()?.toInt()?.coerceAtMost(length) ?: length diff --git a/skriptie/src/commonTest/kotlin/js/EsNumberTest.kt b/skriptie/src/commonTest/kotlin/js/EsNumberTest.kt new file mode 100644 index 00000000..5a4e985e --- /dev/null +++ b/skriptie/src/commonTest/kotlin/js/EsNumberTest.kt @@ -0,0 +1,52 @@ +package js + +import kotlin.test.Test + +class EsNumberTest { + + @Test + fun toFixed(){ + "12.12345.toFixed()".eval().assertEqualsTo("12") + "12.52345.toFixed()".eval().assertEqualsTo("13") + "12.12345.toFixed(1)".eval().assertEqualsTo("12.1") + "12.12345.toFixed(3)".eval().assertEqualsTo("12.123") + "123.456.toFixed(2)".eval().assertEqualsTo("123.46") + "123.51.toFixed(1)".eval().assertEqualsTo("123.5") + } + + @Test + fun toPrecision(){ + val num = 5.123456; + "$num.toPrecision()".eval().assertEqualsTo(5.123456) + "$num.toPrecision(5)".eval().assertEqualsTo(5.1235) + "$num.toPrecision(2)".eval().assertEqualsTo(5.1) + "$num.toPrecision(1)".eval().assertEqualsTo(5.0) + } + + @Test + fun type(){ + "typeof(Number)".eval().assertEqualsTo("function") + "Number(123)".eval().assertEqualsTo(123L) + "Number(123.0)".eval().assertEqualsTo(123.0) + "Number(\"123\")".eval().assertEqualsTo(123L) + "Number(\"123.0\")".eval().assertEqualsTo(123.0) + "Number(\"unicorn\")".eval().assertEqualsTo(Double.NaN) + "Number(undefined)".eval().assertEqualsTo(Double.NaN) + } + @Test + fun static_props(){ + + "Number.MAX_SAFE_INTEGER".eval().assertEqualsTo(Long.MAX_VALUE) + "Number.MIN_SAFE_INTEGER".eval().assertEqualsTo(Long.MIN_VALUE) + "Number.MAX_VALUE".eval().assertEqualsTo(Double.MAX_VALUE) + "Number.EPSILON".eval().assertEqualsTo(Double.MIN_VALUE) + "Number.POSITIVE_INFINITY".eval().assertEqualsTo(Double.POSITIVE_INFINITY) + "Number.NEGATIVE_INFINITY".eval().assertEqualsTo(Double.NEGATIVE_INFINITY) + "Number.NaN".eval().assertEqualsTo(Double.NaN) + } + + @Test + fun static_methods(){ + "Number.isFinite(123)".eval().assertEqualsTo(true) + } +} \ No newline at end of file diff --git a/skriptie/src/commonTest/kotlin/js/JsNumberTest.kt b/skriptie/src/commonTest/kotlin/js/JsNumberTest.kt deleted file mode 100644 index ab613cbd..00000000 --- a/skriptie/src/commonTest/kotlin/js/JsNumberTest.kt +++ /dev/null @@ -1,25 +0,0 @@ -package js - -import kotlin.test.Test - -class JsNumberTest { - - @Test - fun toFixed(){ - "12.12345.toFixed()".eval().assertEqualsTo("12") - "12.52345.toFixed()".eval().assertEqualsTo("13") - "12.12345.toFixed(1)".eval().assertEqualsTo("12.1") - "12.12345.toFixed(3)".eval().assertEqualsTo("12.123") - "123.456.toFixed(2)".eval().assertEqualsTo("123.46") - "123.51.toFixed(1)".eval().assertEqualsTo("123.5") - } - - @Test - fun toPrecision(){ - val num = 5.123456; - "$num.toPrecision()".eval().assertEqualsTo(5.123456) - "$num.toPrecision(5)".eval().assertEqualsTo(5.1235) - "$num.toPrecision(2)".eval().assertEqualsTo(5.1) - "$num.toPrecision(1)".eval().assertEqualsTo(5.0) - } -} \ No newline at end of file diff --git a/skriptie/src/commonTest/kotlin/js/JsTestUtil.kt b/skriptie/src/commonTest/kotlin/js/JsTestUtil.kt index 08d3dd0d..24271321 100644 --- a/skriptie/src/commonTest/kotlin/js/JsTestUtil.kt +++ b/skriptie/src/commonTest/kotlin/js/JsTestUtil.kt @@ -13,12 +13,13 @@ internal fun Any?.assertEqualsTo(other : Double, tolerance: Double = 0.0001) { } internal fun String.eval() : Any? { - val scriptContext = JSRuntime() + val runtime = JSRuntime() + return ScriptEngine( - JSRuntime(), + runtime, EcmascriptInterpreter( JSGlobalContext(false), - scriptContext + runtime ) ).invoke(this) }