Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
Zhirkevich Alexander Y authored and Zhirkevich Alexander Y committed Aug 2, 2024
1 parent e00fd53 commit 56786a1
Show file tree
Hide file tree
Showing 39 changed files with 517 additions and 395 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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<in C : ScriptRuntime> {
public fun invokeRaw(context: C): Any?
public fun interface Expression {
public fun invokeRaw(context: ScriptRuntime): Any?

}

public operator fun <C: ScriptRuntime> Expression<C>.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
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
package io.github.alexzhirkevich.skriptie


public interface InterpretationContext<C : ScriptRuntime> : Expression<C> {
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<C>>?): Expression<C>?
public fun interpret(callable: String?, args: List<Expression>?): Expression?
}

internal fun <C: ScriptRuntime> List<Expression<C>>.argForNameOrIndex(
internal fun List<Expression>.argForNameOrIndex(
index : Int,
vararg name : String,
) : Expression<C>? {
) : Expression? {

return argAtOrNull(index)
// forEach { op ->
Expand All @@ -23,9 +23,9 @@ internal fun <C: ScriptRuntime> List<Expression<C>>.argForNameOrIndex(
// return argAtOrNull(index)
}

internal fun <C: ScriptRuntime> List<Expression<C>>.argAt(
internal fun List<Expression>.argAt(
index : Int,
) : Expression<C> {
) : Expression {

return get(index)
// .let {
Expand All @@ -35,9 +35,9 @@ internal fun <C: ScriptRuntime> List<Expression<C>>.argAt(
// }
}

internal fun <C: ScriptRuntime> List<Expression<C>>.argAtOrNull(
internal fun List<Expression>.argAtOrNull(
index : Int,
) : Expression<C>? {
) : Expression? {

return getOrNull(index)
// /**/.let {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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?
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package io.github.alexzhirkevich.skriptie

public fun interface Script<C : ScriptRuntime> {
public operator fun invoke(context: C): Any?
public fun interface Script {
public operator fun invoke(context: ScriptRuntime): Any?
}

public fun <C : ScriptRuntime> Expression<C>.asScript(): Script<C> = Script { invoke(it) }
public fun Expression.asScript(): Script = Script { invoke(it) }

Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
package io.github.alexzhirkevich.skriptie

public interface ScriptEngine<C : ScriptRuntime> : ScriptInterpreter<C> {
public interface ScriptEngine : ScriptInterpreter {

public val context : C
public val runtime : ScriptRuntime

public fun reset() {
context.reset()
runtime.reset()
}
}

public fun <C : ScriptRuntime> ScriptEngine<C>.invoke(script: String) : Any? {
return interpret(script).invoke(context)
public fun ScriptEngine.invoke(script: String) : Any? {
return interpret(script).invoke(runtime)
}

public fun <C : ScriptRuntime> ScriptEngine(
context: C,
interpreter: ScriptInterpreter<C>
): ScriptEngine<C> = object : ScriptEngine<C>, ScriptInterpreter<C> 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
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package io.github.alexzhirkevich.skriptie

public interface ScriptInterpreter<C : ScriptRuntime> {
public interface ScriptInterpreter {

public fun interpret(script : String) : Script<C>
public fun interpret(script : String) : Script
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,71 +11,72 @@ 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<String, Pair<VariableType, Any?>> = 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<String, Pair<VariableType, Any?>> = mutableMapOf()

private val child by lazy {
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(
Expand All @@ -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)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<C : ScriptRuntime>(
public class FunctionParam(
public val name : String,
public val isVararg : Boolean = false,
public val default : Expression<C>? = null
public val default : Expression? = null
)

internal infix fun <C : ScriptRuntime> String.with(default: Expression<C>?) : FunctionParam<C> {
internal infix fun String.with(default: Expression?) : FunctionParam {
return FunctionParam(this, false, default)
}


internal class OpFunction<C : ScriptRuntime>(
internal interface Callable {
operator fun invoke(args: List<Expression>, context: ScriptRuntime) : Any?
}

internal class Function(
val name : String,
private val parameters : List<FunctionParam<C>>,
private val body : Expression<C>
) {
private val parameters : List<FunctionParam>,
private val body : Expression
) : Callable {
init {
val varargs = parameters.count { it.isVararg }

Expand All @@ -32,9 +36,9 @@ internal class OpFunction<C : ScriptRuntime>(
}
}

fun invoke(
args: List<Expression<C>>,
context: C,
override fun invoke(
args: List<Expression>,
context: ScriptRuntime,
): Any? {
try {
val arguments = buildMap {
Expand All @@ -52,30 +56,28 @@ internal class OpFunction<C : ScriptRuntime>(
)
}
}
return context.withScope(arguments) {
body.invoke(it as C)
}
return context.withScope(arguments, body::invoke)
} catch (ret: BlockReturn) {
return ret.value
}
}
}

internal fun <C : ScriptRuntime> OpFunctionExec(
internal fun OpFunctionExec(
name : String,
receiver : Expression<C>?,
parameters : List<Expression<C>>,
) = Expression<C> { ctx ->
receiver : Expression?,
parameters : List<Expression>,
) = Expression { ctx ->

val function = when (val res = receiver?.invoke(ctx)) {
null -> ctx.getVariable(name)
is ESObject<*> -> res[name]
is ESAny<*> -> {
res as ESAny<C>
null -> ctx.get(name)
is Callable -> res
is ESObject -> res[name]
is ESAny -> {
return@Expression res.invoke(name, ctx, parameters)
}
else -> null
} as? OpFunction<C> ?: unresolvedReference(name)
} as Callable? ?: unresolvedReference(name)

function.invoke(
args = parameters,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<C : ScriptRuntime>(
internal class OpAssign(
val type : VariableType? = null,
val variableName : String,
val assignableValue : Expression<C>,
val assignableValue : Expression,
private val merge : ((Any?, Any?) -> Any?)?
) : Expression<C> {
) : 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"
Expand All @@ -25,8 +25,8 @@ internal class OpAssign<C : ScriptRuntime>(
merge.invoke(current, v)
} else v

context.setVariable(
name = variableName,
context.set(
variable = variableName,
value = value,
type = type
)
Expand Down
Loading

0 comments on commit 56786a1

Please sign in to comment.