Skip to content

Commit

Permalink
Dynamic function calling. (#6713)
Browse files Browse the repository at this point in the history
* Create Script type.

* Support script string/name conversion.

* Script expression.

* Add script lang entry.

* Tests for script expression & names.

* Support all scripts expression.

* Script effects & tests.

* Create dummy Script handle for disabled scripts.

* Script reflection feature flag.

* Restrict literal parsing to commands & parse.

* Test feature flag for resolving name.

* Split ExprScripts by feature to support disabled scripts.

* Fix ExprName tests for new & old behaviour.

* Add tests for disabled script handles.

* Apply suggestions from code review

Co-authored-by: Patrick Miller <[email protected]>

* Improve script loading/unloading safety.

* Add feature check for script hotswapping.

* Use expression stream.

* Conformity for file names and proper loading safety.

* Document validity & add condition support.

* Add script is loaded condition + tests.

* Dynamic function calling + tests.

* Add language entry for types.

* Single-encounter input bootstrapping.

* Apply suggestions from code review

Co-authored-by: sovdee <[email protected]>

* Fix inspection.

* Update src/main/java/ch/njol/skript/expressions/ExprFunction.java

Co-authored-by: sovdee <[email protected]>

* Update src/main/java/ch/njol/skript/expressions/ExprResult.java

Co-authored-by: sovdee <[email protected]>

* Changes from review.

* Fix merge problems.

* Remove script command method usage.

* Fix branch muck.

* Fix more branch muck.

* Fix up ExprName.

* Add docs.

* Add docs.

* Add docs.

* Fix bits.

* Apply suggestions from code review

Co-authored-by: Patrick Miller <[email protected]>

* Fix some bits.

* Function parsing by name.

* Fix up some bits for Walrus.

* Fix merge error.

---------

Co-authored-by: Patrick Miller <[email protected]>
Co-authored-by: sovdee <[email protected]>
  • Loading branch information
3 people committed Dec 31, 2024
1 parent 9acc08a commit 1235d8d
Show file tree
Hide file tree
Showing 21 changed files with 991 additions and 100 deletions.
48 changes: 48 additions & 0 deletions src/main/java/ch/njol/skript/classes/data/SkriptClasses.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import ch.njol.skript.bukkitutil.ItemUtils;
import ch.njol.skript.expressions.base.EventValueExpression;
import ch.njol.skript.lang.ParseContext;
import ch.njol.skript.lang.function.DynamicFunctionReference;
import ch.njol.skript.lang.util.SimpleLiteral;
import ch.njol.skript.localization.Noun;
import ch.njol.skript.localization.RegexMessage;
Expand All @@ -43,6 +44,7 @@
import ch.njol.yggdrasil.Fields;
import org.jetbrains.annotations.NotNull;
import org.skriptlang.skript.lang.script.Script;
import org.skriptlang.skript.util.Executable;

import java.io.StreamCorruptedException;
import java.io.File;
Expand Down Expand Up @@ -722,6 +724,52 @@ public String toVariableNameString(final Script script) {
}
}));

Classes.registerClass(new ClassInfo<>(Executable.class, "executable")
.user("executables?")
.name("Executable")
.description("Something that can be executed (run) and may accept arguments, e.g. a function.",
"This may also return a result.")
.examples("run {_function} with arguments 1 and true")
.since("INSERT VERSION"));

Classes.registerClass(new ClassInfo<>(DynamicFunctionReference.class, "function")
.user("functions?")
.name("Function")
.description("A function loaded by Skript.",
"This can be executed (with arguments) and may return a result.")
.examples("run {_function} with arguments 1 and true",
"set {_result} to the result of {_function}")
.since("INSERT VERSION")
.parser(new Parser<DynamicFunctionReference<?>>() {

@Override
public boolean canParse(final ParseContext context) {
return switch (context) {
case PARSE, COMMAND -> true;
default -> false;
};
}

@Override
@Nullable
public DynamicFunctionReference<?> parse(final String name, final ParseContext context) {
return switch (context) {
case PARSE, COMMAND -> DynamicFunctionReference.parseFunction(name);
default -> null;
};
}

@Override
public String toString(DynamicFunctionReference<?> function, final int flags) {
return function.toString();
}

@Override
public String toVariableNameString(DynamicFunctionReference<?> function) {
return this.toString(function, 0);
}
}));

Classes.registerClass(new AnyInfo<>(AnyNamed.class, "named")
.name("Any Named Thing")
.description("Something that has a name (e.g. an item).")
Expand Down
39 changes: 18 additions & 21 deletions src/main/java/ch/njol/skript/config/Config.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@

import ch.njol.skript.Skript;
import ch.njol.skript.config.validate.SectionValidator;
import ch.njol.skript.lang.util.common.AnyNamed;
import com.google.common.base.Preconditions;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.*;

import java.io.File;
import java.io.IOException;
Expand All @@ -24,14 +23,13 @@
import java.util.Set;

import ch.njol.skript.log.SkriptLogger;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

/**
* Represents a config file.
*/
public class Config implements Comparable<Config> {
public class Config implements Comparable<Config>, AnyNamed {

/**
* One level of the indentation, e.g. a tab or 4 spaces.
Expand Down Expand Up @@ -399,27 +397,26 @@ public String getSaveSeparator() {
return " " + separator + " ";
}

@NotNull String getIndentation() {
return indentation;
}

@NotNull String getIndentationName() {
return indentationName;
}

public @NotNull SectionNode getMainNode() {
return main;
}

public @NotNull String getFileName() {
return fileName;
}

@Override
public int compareTo(@Nullable Config other) {
if (other == null)
return 0;
return fileName.compareTo(other.fileName);
}

/**
* @return The name of this config (excluding path and file extensions)
*/
@Override
public String name() {
String name = this.getFileName();
if (name == null)
return null;
if (name.contains(File.separator))
name = name.substring(name.lastIndexOf(File.separator) + 1);
if (name.contains("."))
return name.substring(0, name.lastIndexOf('.'));
return name;
}

}
9 changes: 8 additions & 1 deletion src/main/java/ch/njol/skript/config/Node.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package ch.njol.skript.config;

import ch.njol.skript.Skript;
import ch.njol.skript.lang.util.common.AnyNamed;
import ch.njol.skript.log.SkriptLogger;
import ch.njol.util.NonNullPair;
import ch.njol.util.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.UnknownNullability;

import java.io.PrintWriter;
import java.util.*;
Expand All @@ -14,7 +16,7 @@
/**
* @author Peter Güttinger
*/
public abstract class Node {
public abstract class Node implements AnyNamed {

@Nullable
protected String key;
Expand Down Expand Up @@ -461,6 +463,11 @@ int getIndex() {
return path.toArray(new String[0]);
}

@Override
public @UnknownNullability String name() {
return this.getKey();
}

@Override
public boolean equals(Object object) {
if (!(object instanceof Node other))
Expand Down
90 changes: 90 additions & 0 deletions src/main/java/ch/njol/skript/effects/EffRun.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package ch.njol.skript.effects;

import ch.njol.skript.Skript;
import ch.njol.skript.doc.*;
import ch.njol.skript.lang.Effect;
import ch.njol.skript.lang.Expression;
import ch.njol.skript.lang.ExpressionList;
import ch.njol.skript.lang.SkriptParser.ParseResult;
import ch.njol.skript.lang.function.DynamicFunctionReference;
import ch.njol.skript.registrations.Feature;
import ch.njol.skript.util.LiteralUtils;
import ch.njol.util.Kleenean;
import org.bukkit.event.Event;
import org.jetbrains.annotations.Nullable;
import org.skriptlang.skript.util.Executable;

@Name("Run (Experimental)")
@Description("Executes a task (a function). Any returned result is discarded.")
@Examples({
"set {_function} to the function named \"myFunction\"",
"run {_function}",
"run {_function} with arguments {_things::*}",
})
@Since("INSERT VERSION")
@Keywords({"run", "execute", "reflection", "function"})
@SuppressWarnings({"rawtypes", "unchecked"})
public class EffRun extends Effect {

static {
Skript.registerEffect(EffRun.class,
"run %executable% [arguments:with arg[ument]s %-objects%]",
"execute %executable% [arguments:with arg[ument]s %-objects%]");
}

// We don't bother with the generic type here because we have no way to verify it
// from the expression, and it makes casting more difficult to no benefit.
private Expression<Executable> executable;
private Expression<?> arguments;
private DynamicFunctionReference.Input input;
private boolean hasArguments;

@Override
public boolean init(Expression<?>[] expressions, int pattern, Kleenean isDelayed, ParseResult result) {
if (!this.getParser().hasExperiment(Feature.SCRIPT_REFLECTION))
return false;
this.executable = ((Expression<Executable>) expressions[0]);
this.hasArguments = result.hasTag("arguments");
if (hasArguments) {
this.arguments = LiteralUtils.defendExpression(expressions[1]);
Expression<?>[] arguments;
if (this.arguments instanceof ExpressionList<?>) {
arguments = ((ExpressionList<?>) this.arguments).getExpressions();
} else {
arguments = new Expression[]{this.arguments};
}
this.input = new DynamicFunctionReference.Input(arguments);
return LiteralUtils.canInitSafely(this.arguments);
} else {
this.input = new DynamicFunctionReference.Input();
}
return true;
}

@Override
protected void execute(Event event) {
Executable task = executable.getSingle(event);
if (task == null)
return;
Object[] arguments;
if (task instanceof DynamicFunctionReference<?> reference) {
Expression<?> validated = reference.validate(input);
if (validated == null)
return;
arguments = validated.getArray(event);
} else if (hasArguments) {
arguments = this.arguments.getArray(event);
} else {
arguments = new Object[0];
}
task.execute(event, arguments);
}

@Override
public String toString(@Nullable Event event, boolean debug) {
if (hasArguments)
return "run " + executable.toString(event, debug) + " with arguments " + arguments.toString(event, debug);
return "run " + executable.toString(event, debug);
}

}
1 change: 1 addition & 0 deletions src/main/java/ch/njol/skript/effects/EffScriptFile.java
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ private void unloadScripts(File file) {
Set<Script> scripts = ScriptLoader.getScripts(file);
if (scripts.isEmpty())
return;
scripts.retainAll(loaded); // skip any that are not loaded (avoid throwing error)
ScriptLoader.unloadScripts(scripts);
} else {
Script script = ScriptLoader.getScript(file);
Expand Down
Loading

0 comments on commit 1235d8d

Please sign in to comment.