From 906412edd5d7e9bafbee0f247ff90a54d8fff056 Mon Sep 17 00:00:00 2001 From: _tud Date: Thu, 12 Oct 2023 21:31:49 +0300 Subject: [PATCH 01/18] Return API --- .../ch/njol/skript/effects/EffReturn.java | 85 ++++++++++++------- .../java/ch/njol/skript/lang/Section.java | 28 +++++- .../java/ch/njol/skript/lang/Trigger.java | 7 +- .../ch/njol/skript/lang/TriggerSection.java | 32 ++++++- .../njol/skript/lang/function/Functions.java | 3 - .../skript/lang/function/ScriptFunction.java | 29 +++---- .../skript/lang/parser/ParserInstance.java | 55 ++++++++++++ 7 files changed, 184 insertions(+), 55 deletions(-) diff --git a/src/main/java/ch/njol/skript/effects/EffReturn.java b/src/main/java/ch/njol/skript/effects/EffReturn.java index 13b563fefee..8db38565ed3 100644 --- a/src/main/java/ch/njol/skript/effects/EffReturn.java +++ b/src/main/java/ch/njol/skript/effects/EffReturn.java @@ -30,9 +30,6 @@ import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.lang.TriggerItem; import ch.njol.skript.lang.TriggerSection; -import ch.njol.skript.lang.function.FunctionEvent; -import ch.njol.skript.lang.function.Functions; -import ch.njol.skript.lang.function.ScriptFunction; import ch.njol.skript.log.RetainingLogHandler; import ch.njol.skript.log.SkriptLogger; import ch.njol.util.Kleenean; @@ -50,70 +47,64 @@ }) @Since("2.2, INSERT VERSION (returns aliases)") public class EffReturn extends Effect { - + static { Skript.registerEffect(EffReturn.class, "return %objects%"); } - + @SuppressWarnings("NotNullFieldNotInitialized") - private ScriptFunction function; - + private TriggerSection section; @SuppressWarnings("NotNullFieldNotInitialized") private Expression value; - - @SuppressWarnings("unchecked") + @Override + @SuppressWarnings("unchecked") public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { - ScriptFunction f = Functions.currentFunction; - if (f == null) { - Skript.error("The return statement can only be used in a function"); + ReturnData data = getParser().getCurrentReturnData(); + if (data == null) { + Skript.error("The return statement cannot be used here"); return false; } - + if (!isDelayed.isFalse()) { Skript.error("A return statement after a delay is useless, as the calling trigger will resume when the delay starts (and won't get any returned value)"); return false; } - - function = f; - ClassInfo returnType = function.getReturnType(); + + section = data.getSection(); + ClassInfo returnType = data.getReturnType(); if (returnType == null) { - Skript.error("This function doesn't return any value. Please use 'stop' or 'exit' if you want to stop the function."); + Skript.error("This trigger doesn't return any value. Please use 'stop' or 'exit' if you want to stop the function."); return false; } - + RetainingLogHandler log = SkriptLogger.startRetainingLog(); Expression convertedExpr; try { convertedExpr = exprs[0].getConvertedExpression(returnType.getC()); if (convertedExpr == null) { - log.printErrors("This function is declared to return " + returnType.getName().withIndefiniteArticle() + ", but " + exprs[0].toString(null, false) + " is not of that type."); + log.printErrors("This trigger is declared to return " + returnType.getName().withIndefiniteArticle() + ", but " + exprs[0].toString(null, false) + " is not of that type."); return false; } log.printLog(); } finally { log.stop(); } - - if (f.isSingle() && !convertedExpr.isSingle()) { - Skript.error("This function is defined to only return a single " + returnType.toString() + ", but this return statement can return multiple values."); + + if (data.isSingle() && !convertedExpr.isSingle()) { + Skript.error("This trigger is defined to only return a single " + returnType + ", but this return statement can return multiple values."); return false; } value = convertedExpr; - + return true; } - + @Override @Nullable - @SuppressWarnings({"unchecked", "rawtypes"}) protected TriggerItem walk(Event event) { debug(event, false); - if (event instanceof FunctionEvent) { - ((ScriptFunction) function).setReturnValue(value.getArray(event)); - } else { - assert false : event; - } + section.setReturnValue(value.getArray(event)); TriggerSection parent = getParent(); while (parent != null) { @@ -125,15 +116,43 @@ protected TriggerItem walk(Event event) { return null; } - + @Override protected void execute(Event event) { assert false; } - + @Override public String toString(@Nullable Event event, boolean debug) { return "return " + value.toString(event, debug); } - + + public static class ReturnData { + + private final TriggerSection section; + @Nullable + private final ClassInfo returnType; + private final boolean single; + + public ReturnData(TriggerSection section, @Nullable ClassInfo returnType, boolean single) { + this.section = section; + this.returnType = returnType; + this.single = single; + } + + public TriggerSection getSection() { + return section; + } + + @Nullable + public ClassInfo getReturnType() { + return returnType; + } + + public boolean isSingle() { + return single; + } + + } + } diff --git a/src/main/java/ch/njol/skript/lang/Section.java b/src/main/java/ch/njol/skript/lang/Section.java index a151e83ab4c..bc9ab659832 100644 --- a/src/main/java/ch/njol/skript/lang/Section.java +++ b/src/main/java/ch/njol/skript/lang/Section.java @@ -20,7 +20,9 @@ import ch.njol.skript.ScriptLoader; import ch.njol.skript.Skript; +import ch.njol.skript.classes.ClassInfo; import ch.njol.skript.config.SectionNode; +import ch.njol.skript.effects.EffReturn.ReturnData; import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.lang.parser.ParserInstance; import ch.njol.util.Kleenean; @@ -29,7 +31,9 @@ import org.skriptlang.skript.lang.structure.Structure; import java.util.ArrayList; +import java.util.Deque; import java.util.Iterator; +import java.util.LinkedList; import java.util.List; import java.util.function.Supplier; @@ -37,7 +41,8 @@ * A section that can decide what it does with its contents, as code isn't parsed by default. *

* In most cases though, a section should load its code through one of the following loading methods: - * {@link #loadCode(SectionNode)}, {@link #loadCode(SectionNode, String, Class[])}, {@link #loadOptionalCode(SectionNode)} + * {@link #loadCode(SectionNode)}, {@link #loadCode(SectionNode, String, Class[])}, {@link #loadOptionalCode(SectionNode)}, + * {@link #loadReturnableCode(SectionNode, ClassInfo, boolean)} *

* Every section must override the {@link TriggerSection#walk(Event)} method. In this method, you can determine whether * or not the section should run. If you have stored a {@link Trigger} from {@link #loadCode(SectionNode, String, Class[])}, you @@ -135,12 +140,14 @@ protected final Trigger loadCode(SectionNode sectionNode, String name, @Nullable Structure previousStructure = parser.getCurrentStructure(); List previousSections = parser.getCurrentSections(); Kleenean previousDelay = parser.getHasDelayBefore(); + Deque previousReturnQueue = parser.getReturnQueue(); parser.setCurrentEvent(name, events); SkriptEvent skriptEvent = new SectionSkriptEvent(name, this); parser.setCurrentStructure(skriptEvent); parser.setCurrentSections(new ArrayList<>()); parser.setHasDelayBefore(Kleenean.FALSE); + parser.setReturnQueue(new LinkedList<>()); List triggerItems = ScriptLoader.loadItems(sectionNode); if (afterLoading != null) @@ -151,6 +158,7 @@ protected final Trigger loadCode(SectionNode sectionNode, String name, @Nullable parser.setCurrentStructure(previousStructure); parser.setCurrentSections(previousSections); parser.setHasDelayBefore(previousDelay); + parser.setReturnQueue(previousReturnQueue); return new Trigger(parser.getCurrentScript(), name, skriptEvent, triggerItems); } @@ -171,6 +179,24 @@ protected void loadOptionalCode(SectionNode sectionNode) { getParser().setHasDelayBefore(Kleenean.UNKNOWN); } + /** + * Loads the code using {@link Section#loadCode(SectionNode)}. + *
+ * This method also accordingly updates {@link ParserInstance#getReturnQueue()} + * to include this trigger with the given return data. + *
+ * This method itself does not modify {@link ParserInstance#getHasDelayBefore()} + * (although the loaded code may change it), the calling code must deal with this. + */ + protected void loadReturnableCode(SectionNode sectionNode, @Nullable ClassInfo returnType, boolean single) { + try { + getParser().pushReturnData(new ReturnData(this, returnType, single)); + loadCode(sectionNode); + } finally { + getParser().popReturnData(); + } + } + @SuppressWarnings({"unchecked", "rawtypes"}) @Nullable public static Section parse(String expr, @Nullable String defaultError, SectionNode sectionNode, List triggerItems) { diff --git a/src/main/java/ch/njol/skript/lang/Trigger.java b/src/main/java/ch/njol/skript/lang/Trigger.java index 50099e1386f..2ece8b973bf 100644 --- a/src/main/java/ch/njol/skript/lang/Trigger.java +++ b/src/main/java/ch/njol/skript/lang/Trigger.java @@ -24,6 +24,7 @@ import org.eclipse.jdt.annotation.Nullable; import java.util.List; +import java.util.function.Function; public class Trigger extends TriggerSection { @@ -36,11 +37,15 @@ public class Trigger extends TriggerSection { private String debugLabel; public Trigger(@Nullable Script script, String name, SkriptEvent event, List items) { - super(items); + this(script, name, event, trigger -> items); + } + + public Trigger(@Nullable Script script, String name, SkriptEvent event, Function> function) { this.script = script; this.name = name; this.event = event; this.debugLabel = "unknown trigger"; + setTriggerItems(function.apply(this)); } /** diff --git a/src/main/java/ch/njol/skript/lang/TriggerSection.java b/src/main/java/ch/njol/skript/lang/TriggerSection.java index fb925f25c3c..88737db58bf 100644 --- a/src/main/java/ch/njol/skript/lang/TriggerSection.java +++ b/src/main/java/ch/njol/skript/lang/TriggerSection.java @@ -20,12 +20,14 @@ import java.util.List; +import ch.njol.util.coll.CollectionUtils; import org.bukkit.event.Event; import org.eclipse.jdt.annotation.Nullable; import ch.njol.skript.ScriptLoader; import ch.njol.skript.config.SectionNode; import ch.njol.skript.lang.parser.ParserInstance; +import org.jetbrains.annotations.ApiStatus; /** * Represents a section of a trigger, e.g. a conditional or a loop @@ -36,7 +38,10 @@ public abstract class TriggerSection extends TriggerItem { protected TriggerItem first = null; @Nullable protected TriggerItem last = null; - + + private boolean returnValueSet = false; + private Object @Nullable [] returnValue; + /** * Reserved for new Trigger(...) */ @@ -93,7 +98,30 @@ public TriggerSection setParent(@Nullable TriggerSection parent) { super.setParent(parent); return this; } - + + public Object @Nullable [] getReturnValue() { + return returnValue; + } + + public T @Nullable [] getReturnValue(Class expectedType) { + return CollectionUtils.arrayType(expectedType).cast(expectedType); + } + + /** + * Should only be called by {@link ch.njol.skript.effects.EffReturn}. + */ + @ApiStatus.Internal + public final void setReturnValue(Object @Nullable [] returnValue) { + assert !returnValueSet; + returnValueSet = true; + this.returnValue = returnValue; + } + + public final void resetReturnValue() { + returnValueSet = false; + returnValue = null; + } + @Override protected final boolean run(Event e) { throw new UnsupportedOperationException(); diff --git a/src/main/java/ch/njol/skript/lang/function/Functions.java b/src/main/java/ch/njol/skript/lang/function/Functions.java index bc19f2bbf72..32027187944 100644 --- a/src/main/java/ch/njol/skript/lang/function/Functions.java +++ b/src/main/java/ch/njol/skript/lang/function/Functions.java @@ -53,9 +53,6 @@ public abstract class Functions { private Functions() {} - @Nullable - public static ScriptFunction currentFunction = null; - /** * Function namespaces. */ diff --git a/src/main/java/ch/njol/skript/lang/function/ScriptFunction.java b/src/main/java/ch/njol/skript/lang/function/ScriptFunction.java index 259c8bcad85..45fd7e6aa1e 100644 --- a/src/main/java/ch/njol/skript/lang/function/ScriptFunction.java +++ b/src/main/java/ch/njol/skript/lang/function/ScriptFunction.java @@ -18,6 +18,7 @@ */ package ch.njol.skript.lang.function; +import ch.njol.skript.lang.parser.ParserInstance; import org.skriptlang.skript.lang.script.Script; import org.eclipse.jdt.annotation.Nullable; @@ -37,32 +38,31 @@ public class ScriptFunction extends Function { public ScriptFunction(Signature sign, Script script, SectionNode node) { super(sign); - - Functions.currentFunction = this; + + ParserInstance parser = ParserInstance.get(); try { trigger = new Trigger( script, "function " + sign.getName(), new SimpleEvent(), - ScriptLoader.loadItems(node) + trigger -> { + parser.pushReturnData(trigger, getReturnType(), isSingle()); + return ScriptLoader.loadItems(node); + } ); - trigger.setLineNumber(node.getLine()); } finally { - Functions.currentFunction = null; + parser.popReturnData(); } + trigger.setLineNumber(node.getLine()); } - private boolean returnValueSet = false; - @Nullable - private T[] returnValue = null; - /** * Should only be called by {@link EffReturn}. + * @deprecated Use {@link ch.njol.skript.lang.TriggerSection#setReturnValue(Object[])} */ + @Deprecated public final void setReturnValue(final @Nullable T[] value) { - assert !returnValueSet; - returnValueSet = true; - returnValue = value; + trigger.setReturnValue(value); } // REMIND track possible types of local variables (including undefined variables) (consider functions, commands, and EffChange) - maybe make a general interface for this purpose @@ -84,13 +84,12 @@ public T[] execute(final FunctionEvent e, final Object[][] params) { } trigger.execute(e); - return returnValue; + return getReturnType() == null ? null : trigger.getReturnValue(getReturnType().getC()); } @Override public boolean resetReturnValue() { - returnValue = null; - returnValueSet = false; + trigger.resetReturnValue(); return true; } diff --git a/src/main/java/ch/njol/skript/lang/parser/ParserInstance.java b/src/main/java/ch/njol/skript/lang/parser/ParserInstance.java index 86d6a209d08..397ff3e4b78 100644 --- a/src/main/java/ch/njol/skript/lang/parser/ParserInstance.java +++ b/src/main/java/ch/njol/skript/lang/parser/ParserInstance.java @@ -20,8 +20,10 @@ import ch.njol.skript.ScriptLoader; import ch.njol.skript.SkriptAPIException; +import ch.njol.skript.classes.ClassInfo; import ch.njol.skript.config.Config; import ch.njol.skript.config.Node; +import ch.njol.skript.effects.EffReturn.ReturnData; import ch.njol.skript.lang.Expression; import ch.njol.skript.lang.SkriptEvent; import ch.njol.skript.lang.SkriptParser; @@ -39,7 +41,9 @@ import java.io.File; import java.util.ArrayList; +import java.util.Deque; import java.util.HashMap; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.function.Function; @@ -356,6 +360,57 @@ public Kleenean getHasDelayBefore() { return hasDelayBefore; } + // Return API + + private Deque returnQueue = new LinkedList<>(); + + public Deque getReturnQueue() { + return returnQueue; + } + + public void setReturnQueue(Deque returnQueue) { + this.returnQueue = returnQueue; + } + + /** + * Retrieves the current {@link ReturnData} + * @return the return data + */ + @Nullable + public ReturnData getCurrentReturnData() { + return returnQueue.peek(); + } + + /** + * Pushes the current trigger onto the return queue. + *
+ * Note: After the trigger finished loading, {@link ParserInstance#popReturnData()} MUST be called + * @param section the current trigger + * @param returnType return type + * @param single whether the trigger can return multiple values or not + */ + public void pushReturnData(TriggerSection section, @Nullable ClassInfo returnType, boolean single) { + pushReturnData(new ReturnData(section, returnType, single)); + } + + /** + * Pushes the current trigger onto the return queue. + *
+ * Note: After the trigger finished loading, {@link ParserInstance#popReturnData()} MUST be called + * @param data the return data + */ + public void pushReturnData(ReturnData data) { + returnQueue.push(data); + } + + /** + * Pops the current trigger off the return queue. Should be called after the trigger has finished loading. + * @see ParserInstance#pushReturnData(ReturnData) + */ + public void popReturnData() { + returnQueue.pop(); + } + // Miscellaneous private final HandlerList handlers = new HandlerList(); From b6df3690ab305af18f3ad47ec6792ab8c9300928 Mon Sep 17 00:00:00 2001 From: _tud Date: Thu, 12 Oct 2023 21:34:44 +0300 Subject: [PATCH 02/18] Re-add `Functions.currentFunction` field for backward compatibility --- src/main/java/ch/njol/skript/lang/function/Functions.java | 3 +++ src/main/java/ch/njol/skript/lang/function/ScriptFunction.java | 2 ++ 2 files changed, 5 insertions(+) diff --git a/src/main/java/ch/njol/skript/lang/function/Functions.java b/src/main/java/ch/njol/skript/lang/function/Functions.java index 32027187944..bc19f2bbf72 100644 --- a/src/main/java/ch/njol/skript/lang/function/Functions.java +++ b/src/main/java/ch/njol/skript/lang/function/Functions.java @@ -53,6 +53,9 @@ public abstract class Functions { private Functions() {} + @Nullable + public static ScriptFunction currentFunction = null; + /** * Function namespaces. */ diff --git a/src/main/java/ch/njol/skript/lang/function/ScriptFunction.java b/src/main/java/ch/njol/skript/lang/function/ScriptFunction.java index 45fd7e6aa1e..1b2d240e103 100644 --- a/src/main/java/ch/njol/skript/lang/function/ScriptFunction.java +++ b/src/main/java/ch/njol/skript/lang/function/ScriptFunction.java @@ -40,6 +40,7 @@ public ScriptFunction(Signature sign, Script script, SectionNode node) { super(sign); ParserInstance parser = ParserInstance.get(); + Functions.currentFunction = this; try { trigger = new Trigger( script, @@ -51,6 +52,7 @@ public ScriptFunction(Signature sign, Script script, SectionNode node) { } ); } finally { + Functions.currentFunction = null; parser.popReturnData(); } trigger.setLineNumber(node.getLine()); From cd7ec472ccccacb5b7234c650f99b6772ad5ab03 Mon Sep 17 00:00:00 2001 From: _tud Date: Thu, 12 Oct 2023 21:35:58 +0300 Subject: [PATCH 03/18] Update EffReturn's description --- src/main/java/ch/njol/skript/effects/EffReturn.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/ch/njol/skript/effects/EffReturn.java b/src/main/java/ch/njol/skript/effects/EffReturn.java index 8db38565ed3..0ec428f8853 100644 --- a/src/main/java/ch/njol/skript/effects/EffReturn.java +++ b/src/main/java/ch/njol/skript/effects/EffReturn.java @@ -37,7 +37,7 @@ import org.eclipse.jdt.annotation.Nullable; @Name("Return") -@Description("Makes a function return a value") +@Description("Makes a trigger (e.g. a function) return a value") @Examples({ "function double(i: number) :: number:", "\treturn 2 * {_i}", From b900f487d07f6986bf2c06cbaa083610a07a2010 Mon Sep 17 00:00:00 2001 From: _tud Date: Fri, 13 Oct 2023 11:32:36 +0300 Subject: [PATCH 04/18] Fix tests --- src/main/java/ch/njol/skript/lang/TriggerSection.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/ch/njol/skript/lang/TriggerSection.java b/src/main/java/ch/njol/skript/lang/TriggerSection.java index 88737db58bf..4789537adab 100644 --- a/src/main/java/ch/njol/skript/lang/TriggerSection.java +++ b/src/main/java/ch/njol/skript/lang/TriggerSection.java @@ -104,7 +104,7 @@ public TriggerSection setParent(@Nullable TriggerSection parent) { } public T @Nullable [] getReturnValue(Class expectedType) { - return CollectionUtils.arrayType(expectedType).cast(expectedType); + return CollectionUtils.arrayType(expectedType).cast(getReturnValue()); } /** From f559dad16476510239a00b39977fbf9430a708e6 Mon Sep 17 00:00:00 2001 From: _tud Date: Tue, 17 Oct 2023 01:08:25 +0300 Subject: [PATCH 05/18] Fix javadocs --- src/main/java/ch/njol/skript/lang/Section.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/main/java/ch/njol/skript/lang/Section.java b/src/main/java/ch/njol/skript/lang/Section.java index bc9ab659832..aa1281b070b 100644 --- a/src/main/java/ch/njol/skript/lang/Section.java +++ b/src/main/java/ch/njol/skript/lang/Section.java @@ -182,11 +182,10 @@ protected void loadOptionalCode(SectionNode sectionNode) { /** * Loads the code using {@link Section#loadCode(SectionNode)}. *
- * This method also accordingly updates {@link ParserInstance#getReturnQueue()} - * to include this trigger with the given return data. - *
- * This method itself does not modify {@link ParserInstance#getHasDelayBefore()} - * (although the loaded code may change it), the calling code must deal with this. + * This method also pushes the current trigger into the return queue, + * and pops it once it's done loading. + * @see ParserInstance#getReturnQueue() + * @see ParserInstance#pushReturnData(ReturnData) */ protected void loadReturnableCode(SectionNode sectionNode, @Nullable ClassInfo returnType, boolean single) { try { From f6a53fb9aef74283ef8811126abac452318cc7a8 Mon Sep 17 00:00:00 2001 From: _tud Date: Tue, 17 Oct 2023 01:14:58 +0300 Subject: [PATCH 06/18] Use the proper term. 'queue' -> 'stack' --- .../java/ch/njol/skript/lang/Section.java | 10 +++++----- .../skript/lang/parser/ParserInstance.java | 20 +++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/main/java/ch/njol/skript/lang/Section.java b/src/main/java/ch/njol/skript/lang/Section.java index aa1281b070b..8e82ea9d926 100644 --- a/src/main/java/ch/njol/skript/lang/Section.java +++ b/src/main/java/ch/njol/skript/lang/Section.java @@ -140,14 +140,14 @@ protected final Trigger loadCode(SectionNode sectionNode, String name, @Nullable Structure previousStructure = parser.getCurrentStructure(); List previousSections = parser.getCurrentSections(); Kleenean previousDelay = parser.getHasDelayBefore(); - Deque previousReturnQueue = parser.getReturnQueue(); + Deque previousReturnQueue = parser.getReturnStack(); parser.setCurrentEvent(name, events); SkriptEvent skriptEvent = new SectionSkriptEvent(name, this); parser.setCurrentStructure(skriptEvent); parser.setCurrentSections(new ArrayList<>()); parser.setHasDelayBefore(Kleenean.FALSE); - parser.setReturnQueue(new LinkedList<>()); + parser.setReturnStack(new LinkedList<>()); List triggerItems = ScriptLoader.loadItems(sectionNode); if (afterLoading != null) @@ -158,7 +158,7 @@ protected final Trigger loadCode(SectionNode sectionNode, String name, @Nullable parser.setCurrentStructure(previousStructure); parser.setCurrentSections(previousSections); parser.setHasDelayBefore(previousDelay); - parser.setReturnQueue(previousReturnQueue); + parser.setReturnStack(previousReturnQueue); return new Trigger(parser.getCurrentScript(), name, skriptEvent, triggerItems); } @@ -182,9 +182,9 @@ protected void loadOptionalCode(SectionNode sectionNode) { /** * Loads the code using {@link Section#loadCode(SectionNode)}. *
- * This method also pushes the current trigger into the return queue, + * This method also pushes the current trigger into the return stack, * and pops it once it's done loading. - * @see ParserInstance#getReturnQueue() + * @see ParserInstance#getReturnStack() * @see ParserInstance#pushReturnData(ReturnData) */ protected void loadReturnableCode(SectionNode sectionNode, @Nullable ClassInfo returnType, boolean single) { diff --git a/src/main/java/ch/njol/skript/lang/parser/ParserInstance.java b/src/main/java/ch/njol/skript/lang/parser/ParserInstance.java index 397ff3e4b78..60f15071ba6 100644 --- a/src/main/java/ch/njol/skript/lang/parser/ParserInstance.java +++ b/src/main/java/ch/njol/skript/lang/parser/ParserInstance.java @@ -362,14 +362,14 @@ public Kleenean getHasDelayBefore() { // Return API - private Deque returnQueue = new LinkedList<>(); + private Deque returnStack = new LinkedList<>(); - public Deque getReturnQueue() { - return returnQueue; + public Deque getReturnStack() { + return returnStack; } - public void setReturnQueue(Deque returnQueue) { - this.returnQueue = returnQueue; + public void setReturnStack(Deque returnStack) { + this.returnStack = returnStack; } /** @@ -378,11 +378,11 @@ public void setReturnQueue(Deque returnQueue) { */ @Nullable public ReturnData getCurrentReturnData() { - return returnQueue.peek(); + return returnStack.peek(); } /** - * Pushes the current trigger onto the return queue. + * Pushes the current trigger onto the return stack. *
* Note: After the trigger finished loading, {@link ParserInstance#popReturnData()} MUST be called * @param section the current trigger @@ -394,13 +394,13 @@ public void pushReturnData(TriggerSection section, @Nullable ClassInfo return } /** - * Pushes the current trigger onto the return queue. + * Pushes the current trigger onto the return stack. *
* Note: After the trigger finished loading, {@link ParserInstance#popReturnData()} MUST be called * @param data the return data */ public void pushReturnData(ReturnData data) { - returnQueue.push(data); + returnStack.push(data); } /** @@ -408,7 +408,7 @@ public void pushReturnData(ReturnData data) { * @see ParserInstance#pushReturnData(ReturnData) */ public void popReturnData() { - returnQueue.pop(); + returnStack.pop(); } // Miscellaneous From ad2f0b19cd706d9853d578eb6c8686e1248a6b23 Mon Sep 17 00:00:00 2001 From: _tud <98935832+UnderscoreTud@users.noreply.github.com> Date: Fri, 10 May 2024 16:49:14 +0300 Subject: [PATCH 07/18] Add missing import --- src/main/java/ch/njol/skript/lang/TriggerSection.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/ch/njol/skript/lang/TriggerSection.java b/src/main/java/ch/njol/skript/lang/TriggerSection.java index f74cc482556..c8e29f66c48 100644 --- a/src/main/java/ch/njol/skript/lang/TriggerSection.java +++ b/src/main/java/ch/njol/skript/lang/TriggerSection.java @@ -21,6 +21,7 @@ import ch.njol.skript.ScriptLoader; import ch.njol.skript.config.SectionNode; import ch.njol.skript.lang.parser.ParserInstance; +import ch.njol.util.coll.CollectionUtils; import org.bukkit.event.Event; import org.eclipse.jdt.annotation.Nullable; import org.jetbrains.annotations.ApiStatus; From 1413626f2dabaef1374287343c2c2eb02f12b79d Mon Sep 17 00:00:00 2001 From: _tud <98935832+UnderscoreTud@users.noreply.github.com> Date: Fri, 10 May 2024 17:40:50 +0300 Subject: [PATCH 08/18] Only exit upto the returnable section when returning rather than the whole trigger --- src/main/java/ch/njol/skript/effects/EffReturn.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/ch/njol/skript/effects/EffReturn.java b/src/main/java/ch/njol/skript/effects/EffReturn.java index 5fc6c5e21a6..052a04127fe 100644 --- a/src/main/java/ch/njol/skript/effects/EffReturn.java +++ b/src/main/java/ch/njol/skript/effects/EffReturn.java @@ -107,13 +107,16 @@ protected TriggerItem walk(Event event) { section.setReturnValue(value.getArray(event)); TriggerSection parent = getParent(); - while (parent != null) { + while (parent != null && parent != section) { if (parent instanceof SectionExitHandler) ((SectionExitHandler) parent).exit(event); parent = parent.getParent(); } + if (section instanceof SectionExitHandler) + ((SectionExitHandler) section).exit(event); + return null; } From 911e5c9ad1ab2053dced5342c8679b7e8fdcee87 Mon Sep 17 00:00:00 2001 From: _tud <98935832+UnderscoreTud@users.noreply.github.com> Date: Fri, 10 May 2024 17:43:20 +0300 Subject: [PATCH 09/18] Requested Changes --- .../ch/njol/skript/effects/EffReturn.java | 5 ++- .../java/ch/njol/skript/lang/Section.java | 23 +++++++++++-- .../java/ch/njol/skript/lang/Trigger.java | 4 +-- .../ch/njol/skript/lang/TriggerSection.java | 24 +++++++------- .../skript/lang/function/ScriptFunction.java | 32 +++++++++---------- .../skript/lang/parser/ParserInstance.java | 8 +++-- 6 files changed, 59 insertions(+), 37 deletions(-) diff --git a/src/main/java/ch/njol/skript/effects/EffReturn.java b/src/main/java/ch/njol/skript/effects/EffReturn.java index 052a04127fe..72b440bfef1 100644 --- a/src/main/java/ch/njol/skript/effects/EffReturn.java +++ b/src/main/java/ch/njol/skript/effects/EffReturn.java @@ -104,7 +104,7 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye @Nullable protected TriggerItem walk(Event event) { debug(event, false); - section.setReturnValue(value.getArray(event)); + section.setReturnValues(value.getArray(event)); TriggerSection parent = getParent(); while (parent != null && parent != section) { @@ -130,6 +130,9 @@ public String toString(@Nullable Event event, boolean debug) { return "return " + value.toString(event, debug); } + /** + * Used to define the return data of a trigger. + */ public static class ReturnData { private final TriggerSection section; diff --git a/src/main/java/ch/njol/skript/lang/Section.java b/src/main/java/ch/njol/skript/lang/Section.java index 3f8eecb8680..0a33bca5eb1 100644 --- a/src/main/java/ch/njol/skript/lang/Section.java +++ b/src/main/java/ch/njol/skript/lang/Section.java @@ -139,7 +139,7 @@ protected final Trigger loadCode(SectionNode sectionNode, String name, @Nullable Structure previousStructure = parser.getCurrentStructure(); List previousSections = parser.getCurrentSections(); Kleenean previousDelay = parser.getHasDelayBefore(); - Deque previousReturnQueue = parser.getReturnStack(); + Deque previousReturnStack = parser.getReturnStack(); parser.setCurrentEvent(name, events); SkriptEvent skriptEvent = new SectionSkriptEvent(name, this); @@ -157,7 +157,7 @@ protected final Trigger loadCode(SectionNode sectionNode, String name, @Nullable parser.setCurrentStructure(previousStructure); parser.setCurrentSections(previousSections); parser.setHasDelayBefore(previousDelay); - parser.setReturnStack(previousReturnQueue); + parser.setReturnStack(previousReturnStack); return new Trigger(parser.getCurrentScript(), name, skriptEvent, triggerItems); } @@ -181,7 +181,7 @@ protected void loadOptionalCode(SectionNode sectionNode) { /** * Loads the code using {@link Section#loadCode(SectionNode)}. *
- * This method also pushes the current trigger into the return stack, + * This method also pushes the current trigger onto the return stack, * and pops it once it's done loading. * @see ParserInstance#getReturnStack() * @see ParserInstance#pushReturnData(ReturnData) @@ -195,6 +195,23 @@ protected void loadReturnableCode(SectionNode sectionNode, @Nullable ClassInfo + * This method also pushes the current trigger onto the return stack, + * and pops it once it's done loading. + * @see ParserInstance#getReturnStack() + * @see ParserInstance#pushReturnData(ReturnData) + */ + protected void loadReturnableCode(SectionNode sectionNode, ReturnData data) { + try { + getParser().pushReturnData(data); + loadCode(sectionNode); + } finally { + getParser().popReturnData(); + } + } + @Nullable @SuppressWarnings({"unchecked", "rawtypes"}) public static Section parse(String expr, @Nullable String defaultError, SectionNode sectionNode, List triggerItems) { diff --git a/src/main/java/ch/njol/skript/lang/Trigger.java b/src/main/java/ch/njol/skript/lang/Trigger.java index 8c454142b28..e279e56274d 100644 --- a/src/main/java/ch/njol/skript/lang/Trigger.java +++ b/src/main/java/ch/njol/skript/lang/Trigger.java @@ -40,12 +40,12 @@ public Trigger(@Nullable Script script, String name, SkriptEvent event, List items); } - public Trigger(@Nullable Script script, String name, SkriptEvent event, Function> function) { + public Trigger(@Nullable Script script, String name, SkriptEvent event, Function> loadItems) { this.script = script; this.name = name; this.event = event; this.debugLabel = "unknown trigger"; - setTriggerItems(function.apply(this)); + setTriggerItems(loadItems.apply(this)); } /** diff --git a/src/main/java/ch/njol/skript/lang/TriggerSection.java b/src/main/java/ch/njol/skript/lang/TriggerSection.java index c8e29f66c48..feef2de72f0 100644 --- a/src/main/java/ch/njol/skript/lang/TriggerSection.java +++ b/src/main/java/ch/njol/skript/lang/TriggerSection.java @@ -21,10 +21,9 @@ import ch.njol.skript.ScriptLoader; import ch.njol.skript.config.SectionNode; import ch.njol.skript.lang.parser.ParserInstance; -import ch.njol.util.coll.CollectionUtils; import org.bukkit.event.Event; import org.eclipse.jdt.annotation.Nullable; -import org.jetbrains.annotations.ApiStatus; +import org.skriptlang.skript.lang.converter.Converters; import java.util.List; @@ -36,7 +35,7 @@ public abstract class TriggerSection extends TriggerItem { @Nullable protected TriggerItem first, last; - private boolean returnValueSet = false; + private boolean returnValueSet; private Object @Nullable [] returnValue; /** @@ -96,25 +95,26 @@ public TriggerSection setParent(@Nullable TriggerSection parent) { return this; } - public Object @Nullable [] getReturnValue() { + public Object @Nullable [] getReturnValues() { return returnValue; } - public T @Nullable [] getReturnValue(Class expectedType) { - return CollectionUtils.arrayType(expectedType).cast(getReturnValue()); - } - /** - * Should only be called by {@link ch.njol.skript.effects.EffReturn}. + * Returns the return values of the trigger execution, converting them to the specified type. + * @param expectedType the type to convert to. + * @return the return values. May be null if no return values were provided. */ - @ApiStatus.Internal - public final void setReturnValue(Object @Nullable [] returnValue) { + public T @Nullable [] getReturnValues(Class expectedType) { + return Converters.convert(getReturnValues(), expectedType); + } + + public final void setReturnValues(Object @Nullable [] returnValue) { assert !returnValueSet; returnValueSet = true; this.returnValue = returnValue; } - public final void resetReturnValue() { + public final void resetReturnValues() { returnValueSet = false; returnValue = null; } diff --git a/src/main/java/ch/njol/skript/lang/function/ScriptFunction.java b/src/main/java/ch/njol/skript/lang/function/ScriptFunction.java index 1b2d240e103..df4a41012df 100644 --- a/src/main/java/ch/njol/skript/lang/function/ScriptFunction.java +++ b/src/main/java/ch/njol/skript/lang/function/ScriptFunction.java @@ -18,7 +18,9 @@ */ package ch.njol.skript.lang.function; +import ch.njol.skript.classes.ClassInfo; import ch.njol.skript.lang.parser.ParserInstance; +import org.jetbrains.annotations.ApiStatus; import org.skriptlang.skript.lang.script.Script; import org.eclipse.jdt.annotation.Nullable; @@ -29,9 +31,6 @@ import ch.njol.skript.lang.util.SimpleEvent; import ch.njol.skript.variables.Variables; -/** - * @author Peter Güttinger - */ public class ScriptFunction extends Function { private final Trigger trigger; @@ -58,20 +57,10 @@ public ScriptFunction(Signature sign, Script script, SectionNode node) { trigger.setLineNumber(node.getLine()); } - /** - * Should only be called by {@link EffReturn}. - * @deprecated Use {@link ch.njol.skript.lang.TriggerSection#setReturnValue(Object[])} - */ - @Deprecated - public final void setReturnValue(final @Nullable T[] value) { - trigger.setReturnValue(value); - } - // REMIND track possible types of local variables (including undefined variables) (consider functions, commands, and EffChange) - maybe make a general interface for this purpose // REM: use patterns, e.g. {_a%b%} is like "a.*", and thus subsequent {_axyz} may be set and of that type. @Override - @Nullable - public T[] execute(final FunctionEvent e, final Object[][] params) { + public T @Nullable [] execute(final FunctionEvent e, final Object[][] params) { Parameter[] parameters = getSignature().getParameters(); for (int i = 0; i < parameters.length; i++) { Parameter p = parameters[i]; @@ -86,12 +75,23 @@ public T[] execute(final FunctionEvent e, final Object[][] params) { } trigger.execute(e); - return getReturnType() == null ? null : trigger.getReturnValue(getReturnType().getC()); + ClassInfo returnType = getReturnType(); + return returnType != null ? trigger.getReturnValues(returnType.getC()) : null; + } + + /** + * Should only be called by {@link EffReturn}. + * @deprecated Use {@link ch.njol.skript.lang.TriggerSection#setReturnValues(Object[])} + */ + @Deprecated + @ApiStatus.Internal + public final void setReturnValue(final @Nullable T[] value) { + trigger.setReturnValues(value); } @Override public boolean resetReturnValue() { - trigger.resetReturnValue(); + trigger.resetReturnValues(); return true; } diff --git a/src/main/java/ch/njol/skript/lang/parser/ParserInstance.java b/src/main/java/ch/njol/skript/lang/parser/ParserInstance.java index 60f15071ba6..621b0bac958 100644 --- a/src/main/java/ch/njol/skript/lang/parser/ParserInstance.java +++ b/src/main/java/ch/njol/skript/lang/parser/ParserInstance.java @@ -398,17 +398,19 @@ public void pushReturnData(TriggerSection section, @Nullable ClassInfo return *
* Note: After the trigger finished loading, {@link ParserInstance#popReturnData()} MUST be called * @param data the return data + * @see ParserInstance#popReturnData() */ public void pushReturnData(ReturnData data) { returnStack.push(data); } /** - * Pops the current trigger off the return queue. Should be called after the trigger has finished loading. + * Pops the current trigger off the return stack. Should be called after the trigger has finished loading. + * @return the popped return data * @see ParserInstance#pushReturnData(ReturnData) */ - public void popReturnData() { - returnStack.pop(); + public ReturnData popReturnData() { + return returnStack.pop(); } // Miscellaneous From 4e3a466412e4402e9c378cb2469363e287d51350 Mon Sep 17 00:00:00 2001 From: _tud <98935832+UnderscoreTud@users.noreply.github.com> Date: Mon, 13 May 2024 07:24:56 +0300 Subject: [PATCH 10/18] change `function` to `trigger` --- src/main/java/ch/njol/skript/effects/EffReturn.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/ch/njol/skript/effects/EffReturn.java b/src/main/java/ch/njol/skript/effects/EffReturn.java index 72b440bfef1..76d2cf3d272 100644 --- a/src/main/java/ch/njol/skript/effects/EffReturn.java +++ b/src/main/java/ch/njol/skript/effects/EffReturn.java @@ -74,7 +74,7 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye section = data.getSection(); ClassInfo returnType = data.getReturnType(); if (returnType == null) { - Skript.error("This trigger doesn't return any value. Please use 'stop' or 'exit' if you want to stop the function."); + Skript.error("This trigger doesn't return any value. Please use 'stop' or 'exit' if you want to stop the trigger."); return false; } From 0056f44238343b8b18d129d122c15dcf434b217c Mon Sep 17 00:00:00 2001 From: _tud <98935832+UnderscoreTud@users.noreply.github.com> Date: Mon, 13 May 2024 07:35:48 +0300 Subject: [PATCH 11/18] Make TriggerSection#setReturnValues and TriggerSection#resetReturnValues non-final --- src/main/java/ch/njol/skript/lang/TriggerSection.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/ch/njol/skript/lang/TriggerSection.java b/src/main/java/ch/njol/skript/lang/TriggerSection.java index feef2de72f0..1a0e5e67dbf 100644 --- a/src/main/java/ch/njol/skript/lang/TriggerSection.java +++ b/src/main/java/ch/njol/skript/lang/TriggerSection.java @@ -108,13 +108,13 @@ public TriggerSection setParent(@Nullable TriggerSection parent) { return Converters.convert(getReturnValues(), expectedType); } - public final void setReturnValues(Object @Nullable [] returnValue) { + public void setReturnValues(Object @Nullable [] returnValue) { assert !returnValueSet; returnValueSet = true; this.returnValue = returnValue; } - public final void resetReturnValues() { + public void resetReturnValues() { returnValueSet = false; returnValue = null; } From f09b0c104fe7d8f78c328924e503f00d6aaf007a Mon Sep 17 00:00:00 2001 From: _tud <98935832+UnderscoreTud@users.noreply.github.com> Date: Tue, 14 May 2024 04:24:05 +0300 Subject: [PATCH 12/18] Replace `ReturnData` with a new interface --- .../ch/njol/skript/effects/EffReturn.java | 55 ++---- .../ch/njol/skript/lang/ReturnHandler.java | 175 ++++++++++++++++++ .../njol/skript/lang/ReturnableTrigger.java | 54 ++++++ .../java/ch/njol/skript/lang/Section.java | 43 +---- .../java/ch/njol/skript/lang/Trigger.java | 7 +- .../ch/njol/skript/lang/TriggerSection.java | 28 --- .../njol/skript/lang/function/Functions.java | 2 +- .../skript/lang/function/ScriptFunction.java | 62 ++++--- .../skript/lang/parser/ParserInstance.java | 57 ------ 9 files changed, 286 insertions(+), 197 deletions(-) create mode 100644 src/main/java/ch/njol/skript/lang/ReturnHandler.java create mode 100644 src/main/java/ch/njol/skript/lang/ReturnableTrigger.java diff --git a/src/main/java/ch/njol/skript/effects/EffReturn.java b/src/main/java/ch/njol/skript/effects/EffReturn.java index 76d2cf3d272..50b2dc9ccb7 100644 --- a/src/main/java/ch/njol/skript/effects/EffReturn.java +++ b/src/main/java/ch/njol/skript/effects/EffReturn.java @@ -26,10 +26,13 @@ import ch.njol.skript.doc.Since; import ch.njol.skript.lang.Effect; import ch.njol.skript.lang.Expression; +import ch.njol.skript.lang.ReturnHandler; +import ch.njol.skript.lang.ReturnHandler.ReturnHandlerStack; import ch.njol.skript.lang.SectionExitHandler; import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.lang.TriggerItem; import ch.njol.skript.lang.TriggerSection; +import ch.njol.skript.lang.parser.ParserInstance; import ch.njol.skript.log.RetainingLogHandler; import ch.njol.skript.log.SkriptLogger; import ch.njol.util.Kleenean; @@ -50,18 +53,19 @@ public class EffReturn extends Effect { static { Skript.registerEffect(EffReturn.class, "return %objects%"); + ParserInstance.registerData(ReturnHandlerStack.class, ReturnHandlerStack::new); } @SuppressWarnings("NotNullFieldNotInitialized") - private TriggerSection section; + private ReturnHandler handler; @SuppressWarnings("NotNullFieldNotInitialized") private Expression value; @Override @SuppressWarnings("unchecked") public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { - ReturnData data = getParser().getCurrentReturnData(); - if (data == null) { + handler = getParser().getData(ReturnHandlerStack.class).getCurrentHandler(); + if (handler == null) { Skript.error("The return statement cannot be used here"); return false; } @@ -71,8 +75,7 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye return false; } - section = data.getSection(); - ClassInfo returnType = data.getReturnType(); + ClassInfo returnType = handler.returnValueType(); if (returnType == null) { Skript.error("This trigger doesn't return any value. Please use 'stop' or 'exit' if you want to stop the trigger."); return false; @@ -91,7 +94,7 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye log.stop(); } - if (data.isSingle() && !convertedExpr.isSingle()) { + if (handler.singleReturnValue() && !convertedExpr.isSingle()) { Skript.error("This trigger is defined to only return a single " + returnType + ", but this return statement can return multiple values."); return false; } @@ -104,18 +107,19 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye @Nullable protected TriggerItem walk(Event event) { debug(event, false); - section.setReturnValues(value.getArray(event)); + //noinspection rawtypes,unchecked + ((ReturnHandler) handler).returnValues(value.getArray(event)); TriggerSection parent = getParent(); - while (parent != null && parent != section) { + while (parent != null && parent != handler) { if (parent instanceof SectionExitHandler) ((SectionExitHandler) parent).exit(event); parent = parent.getParent(); } - if (section instanceof SectionExitHandler) - ((SectionExitHandler) section).exit(event); + if (handler instanceof SectionExitHandler) + ((SectionExitHandler) handler).exit(event); return null; } @@ -130,35 +134,4 @@ public String toString(@Nullable Event event, boolean debug) { return "return " + value.toString(event, debug); } - /** - * Used to define the return data of a trigger. - */ - public static class ReturnData { - - private final TriggerSection section; - @Nullable - private final ClassInfo returnType; - private final boolean single; - - public ReturnData(TriggerSection section, @Nullable ClassInfo returnType, boolean single) { - this.section = section; - this.returnType = returnType; - this.single = single; - } - - public TriggerSection getSection() { - return section; - } - - @Nullable - public ClassInfo getReturnType() { - return returnType; - } - - public boolean isSingle() { - return single; - } - - } - } diff --git a/src/main/java/ch/njol/skript/lang/ReturnHandler.java b/src/main/java/ch/njol/skript/lang/ReturnHandler.java new file mode 100644 index 00000000000..1441de032a9 --- /dev/null +++ b/src/main/java/ch/njol/skript/lang/ReturnHandler.java @@ -0,0 +1,175 @@ +/** + * This file is part of Skript. + * + * Skript is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Skript is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Skript. If not, see . + * + * Copyright Peter Güttinger, SkriptLang team and contributors + */ +package ch.njol.skript.lang; + +import ch.njol.skript.ScriptLoader; +import ch.njol.skript.SkriptAPIException; +import ch.njol.skript.classes.ClassInfo; +import ch.njol.skript.config.SectionNode; +import ch.njol.skript.lang.parser.ParserInstance; +import ch.njol.skript.lang.util.SimpleEvent; +import ch.njol.util.Kleenean; +import org.bukkit.event.Event; +import org.eclipse.jdt.annotation.Nullable; +import org.jetbrains.annotations.ApiStatus.NonExtendable; +import org.skriptlang.skript.lang.structure.Structure; + +import java.util.ArrayList; +import java.util.Deque; +import java.util.LinkedList; +import java.util.List; + +public interface ReturnHandler { + + @NonExtendable + default void loadReturnableSectionCode(SectionNode node) { + if (!(this instanceof TriggerSection)) + throw new SkriptAPIException("loadReturnableSectionCode called on a non-section object"); + ParserInstance parser = ParserInstance.get(); + ReturnHandlerStack stack = parser.getData(ReturnHandlerStack.class); + stack.push(this); + TriggerSection section = (TriggerSection) this; + List currentSections = parser.getCurrentSections(); + currentSections.add(section); + try { + section.setTriggerItems(ScriptLoader.loadItems(node)); + } finally { + currentSections.remove(currentSections.size() - 1); + stack.pop(); + } + } + + @NonExtendable + default Trigger loadReturnableTrigger(SectionNode node, String name, Class[] events) { + ParserInstance parser = ParserInstance.get(); + ReturnHandlerStack stack = parser.getData(ReturnHandlerStack.class); + boolean isSection = this instanceof Section; + SkriptEvent skriptEvent = isSection ? new SectionSkriptEvent(name, (Section) this) : new SimpleEvent(); + String previousName = null; + Class[] previousEvents = null; + Structure previousStructure = null; + List previousSections = null; + Kleenean previousDelay = null; + Deque> previousReturnStack = null; + if (isSection) { + previousName = parser.getCurrentEventName(); + previousEvents = parser.getCurrentEvents(); + previousStructure = parser.getCurrentStructure(); + previousSections = parser.getCurrentSections(); + previousDelay = parser.getHasDelayBefore(); + + parser.setCurrentEvent(name, events); + parser.setCurrentStructure(skriptEvent); + parser.setCurrentSections(new ArrayList<>()); + parser.setHasDelayBefore(Kleenean.FALSE); + } + try { + // push handler + return new ReturnableTrigger( + this, + parser.getCurrentScript(), + name, + skriptEvent, + trigger -> { + stack.push(trigger); + return ScriptLoader.loadItems(node); + } + ); + } finally { + stack.pop(); + if (isSection) { + parser.setCurrentEvent(previousName, previousEvents); + parser.setCurrentStructure(previousStructure); + parser.setCurrentSections(previousSections); + parser.setHasDelayBefore(previousDelay); + } + } + } + + @NonExtendable + default Trigger loadReturnableTrigger(SectionNode node, String name, SkriptEvent event) { + ParserInstance parser = ParserInstance.get(); + ReturnHandlerStack stack = parser.getData(ReturnHandlerStack.class); + try { + return new ReturnableTrigger( + this, + parser.getCurrentScript(), + name, + event, + trigger -> { + stack.push(trigger); + return ScriptLoader.loadItems(node); + } + ); + } finally { + stack.pop(); + } + } + + void returnValues(T @Nullable [] values); + + boolean singleReturnValue(); + + @Nullable ClassInfo returnValueType(); + + class ReturnHandlerStack extends ParserInstance.Data { + + private final Deque> stack = new LinkedList<>(); + + public ReturnHandlerStack(ParserInstance parserInstance) { + super(parserInstance); + } + + public Deque> getStack() { + return stack; + } + + /** + * Retrieves the current {@link ReturnHandler} + * @return the return data + */ + public @Nullable ReturnHandler getCurrentHandler() { + return stack.peek(); + } + + /** + * Pushes the current return handler onto the return stack. + *
+ * Note: After the trigger finished loading, + * {@link ReturnHandlerStack#pop()} MUST be called + * @param handler the return handler + * @see ReturnHandlerStack#pop() + */ + public void push(ReturnHandler handler) { + stack.push(handler); + } + + /** + * Pops the current handler off the return stack. + * Should be called after the trigger has finished loading. + * @return the popped return data + * @see ReturnHandlerStack#push(ReturnHandler) + */ + public ReturnHandler pop() { + return stack.pop(); + } + + } + +} diff --git a/src/main/java/ch/njol/skript/lang/ReturnableTrigger.java b/src/main/java/ch/njol/skript/lang/ReturnableTrigger.java new file mode 100644 index 00000000000..c79e397db95 --- /dev/null +++ b/src/main/java/ch/njol/skript/lang/ReturnableTrigger.java @@ -0,0 +1,54 @@ +/** + * This file is part of Skript. + * + * Skript is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Skript is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Skript. If not, see . + * + * Copyright Peter Güttinger, SkriptLang team and contributors + */ +package ch.njol.skript.lang; + +import ch.njol.skript.classes.ClassInfo; +import org.eclipse.jdt.annotation.Nullable; +import org.skriptlang.skript.lang.script.Script; + +import java.util.Collections; +import java.util.List; +import java.util.function.Function; + +public class ReturnableTrigger extends Trigger implements ReturnHandler { + + private final ReturnHandler handler; + + public ReturnableTrigger(ReturnHandler handler, @Nullable Script script, String name, SkriptEvent event, Function, List> loadItems) { + super(script, name, event, Collections.emptyList()); + this.handler = handler; + setTriggerItems(loadItems.apply(this)); + } + + @Override + public void returnValues(T @Nullable [] values) { + handler.returnValues(values); + } + + @Override + public boolean singleReturnValue() { + return handler.singleReturnValue(); + } + + @Override + public @Nullable ClassInfo returnValueType() { + return handler.returnValueType(); + } + +} diff --git a/src/main/java/ch/njol/skript/lang/Section.java b/src/main/java/ch/njol/skript/lang/Section.java index 0a33bca5eb1..0a8b3a58f63 100644 --- a/src/main/java/ch/njol/skript/lang/Section.java +++ b/src/main/java/ch/njol/skript/lang/Section.java @@ -20,9 +20,7 @@ import ch.njol.skript.ScriptLoader; import ch.njol.skript.Skript; -import ch.njol.skript.classes.ClassInfo; import ch.njol.skript.config.SectionNode; -import ch.njol.skript.effects.EffReturn.ReturnData; import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.lang.parser.ParserInstance; import ch.njol.util.Kleenean; @@ -31,9 +29,7 @@ import org.skriptlang.skript.lang.structure.Structure; import java.util.ArrayList; -import java.util.Deque; import java.util.Iterator; -import java.util.LinkedList; import java.util.List; import java.util.function.Supplier; @@ -42,7 +38,7 @@ *

* In most cases though, a section should load its code through one of the following loading methods: * {@link #loadCode(SectionNode)}, {@link #loadCode(SectionNode, String, Class[])}, {@link #loadOptionalCode(SectionNode)}, - * {@link #loadReturnableCode(SectionNode, ClassInfo, boolean)} + * {@link ReturnHandler#loadReturnableSectionCode(SectionNode)} *

* Every section must override the {@link TriggerSection#walk(Event)} method. In this method, you can determine whether * the section should run. If you have stored a {@link Trigger} from {@link #loadCode(SectionNode, String, Class[])}, you * should not run it with this event passed in this walk method. @@ -139,14 +135,12 @@ protected final Trigger loadCode(SectionNode sectionNode, String name, @Nullable Structure previousStructure = parser.getCurrentStructure(); List previousSections = parser.getCurrentSections(); Kleenean previousDelay = parser.getHasDelayBefore(); - Deque previousReturnStack = parser.getReturnStack(); parser.setCurrentEvent(name, events); SkriptEvent skriptEvent = new SectionSkriptEvent(name, this); parser.setCurrentStructure(skriptEvent); parser.setCurrentSections(new ArrayList<>()); parser.setHasDelayBefore(Kleenean.FALSE); - parser.setReturnStack(new LinkedList<>()); List triggerItems = ScriptLoader.loadItems(sectionNode); if (afterLoading != null) @@ -157,7 +151,6 @@ protected final Trigger loadCode(SectionNode sectionNode, String name, @Nullable parser.setCurrentStructure(previousStructure); parser.setCurrentSections(previousSections); parser.setHasDelayBefore(previousDelay); - parser.setReturnStack(previousReturnStack); return new Trigger(parser.getCurrentScript(), name, skriptEvent, triggerItems); } @@ -178,40 +171,6 @@ protected void loadOptionalCode(SectionNode sectionNode) { getParser().setHasDelayBefore(Kleenean.UNKNOWN); } - /** - * Loads the code using {@link Section#loadCode(SectionNode)}. - *
- * This method also pushes the current trigger onto the return stack, - * and pops it once it's done loading. - * @see ParserInstance#getReturnStack() - * @see ParserInstance#pushReturnData(ReturnData) - */ - protected void loadReturnableCode(SectionNode sectionNode, @Nullable ClassInfo returnType, boolean single) { - try { - getParser().pushReturnData(new ReturnData(this, returnType, single)); - loadCode(sectionNode); - } finally { - getParser().popReturnData(); - } - } - - /** - * Loads the code using {@link Section#loadCode(SectionNode)}. - *
- * This method also pushes the current trigger onto the return stack, - * and pops it once it's done loading. - * @see ParserInstance#getReturnStack() - * @see ParserInstance#pushReturnData(ReturnData) - */ - protected void loadReturnableCode(SectionNode sectionNode, ReturnData data) { - try { - getParser().pushReturnData(data); - loadCode(sectionNode); - } finally { - getParser().popReturnData(); - } - } - @Nullable @SuppressWarnings({"unchecked", "rawtypes"}) public static Section parse(String expr, @Nullable String defaultError, SectionNode sectionNode, List triggerItems) { diff --git a/src/main/java/ch/njol/skript/lang/Trigger.java b/src/main/java/ch/njol/skript/lang/Trigger.java index e279e56274d..b9092703247 100644 --- a/src/main/java/ch/njol/skript/lang/Trigger.java +++ b/src/main/java/ch/njol/skript/lang/Trigger.java @@ -24,7 +24,6 @@ import org.eclipse.jdt.annotation.Nullable; import java.util.List; -import java.util.function.Function; public class Trigger extends TriggerSection { @@ -37,15 +36,11 @@ public class Trigger extends TriggerSection { private String debugLabel; public Trigger(@Nullable Script script, String name, SkriptEvent event, List items) { - this(script, name, event, trigger -> items); - } - - public Trigger(@Nullable Script script, String name, SkriptEvent event, Function> loadItems) { + super(items); this.script = script; this.name = name; this.event = event; this.debugLabel = "unknown trigger"; - setTriggerItems(loadItems.apply(this)); } /** diff --git a/src/main/java/ch/njol/skript/lang/TriggerSection.java b/src/main/java/ch/njol/skript/lang/TriggerSection.java index 1a0e5e67dbf..d1343d43605 100644 --- a/src/main/java/ch/njol/skript/lang/TriggerSection.java +++ b/src/main/java/ch/njol/skript/lang/TriggerSection.java @@ -23,7 +23,6 @@ import ch.njol.skript.lang.parser.ParserInstance; import org.bukkit.event.Event; import org.eclipse.jdt.annotation.Nullable; -import org.skriptlang.skript.lang.converter.Converters; import java.util.List; @@ -35,9 +34,6 @@ public abstract class TriggerSection extends TriggerItem { @Nullable protected TriggerItem first, last; - private boolean returnValueSet; - private Object @Nullable [] returnValue; - /** * Reserved for new Trigger(...) */ @@ -95,30 +91,6 @@ public TriggerSection setParent(@Nullable TriggerSection parent) { return this; } - public Object @Nullable [] getReturnValues() { - return returnValue; - } - - /** - * Returns the return values of the trigger execution, converting them to the specified type. - * @param expectedType the type to convert to. - * @return the return values. May be null if no return values were provided. - */ - public T @Nullable [] getReturnValues(Class expectedType) { - return Converters.convert(getReturnValues(), expectedType); - } - - public void setReturnValues(Object @Nullable [] returnValue) { - assert !returnValueSet; - returnValueSet = true; - this.returnValue = returnValue; - } - - public void resetReturnValues() { - returnValueSet = false; - returnValue = null; - } - @Override protected final boolean run(Event event) { throw new UnsupportedOperationException(); diff --git a/src/main/java/ch/njol/skript/lang/function/Functions.java b/src/main/java/ch/njol/skript/lang/function/Functions.java index bc19f2bbf72..2eee7ea6c18 100644 --- a/src/main/java/ch/njol/skript/lang/function/Functions.java +++ b/src/main/java/ch/njol/skript/lang/function/Functions.java @@ -121,7 +121,7 @@ public static Function loadFunction(Script script, SectionNode node, Signatur Skript.debug((signature.local ? "local " : "") + "function " + name + "(" + StringUtils.join(params, ", ") + ")" + (c != null ? " :: " + (signature.isSingle() ? c.getName().getSingular() : c.getName().getPlural()) : "") + ":"); - Function f = new ScriptFunction<>(signature, script, node); + Function f = new ScriptFunction<>(signature, node); // Register the function for signature namespace.addFunction(f); diff --git a/src/main/java/ch/njol/skript/lang/function/ScriptFunction.java b/src/main/java/ch/njol/skript/lang/function/ScriptFunction.java index df4a41012df..40a009c9122 100644 --- a/src/main/java/ch/njol/skript/lang/function/ScriptFunction.java +++ b/src/main/java/ch/njol/skript/lang/function/ScriptFunction.java @@ -19,44 +19,44 @@ package ch.njol.skript.lang.function; import ch.njol.skript.classes.ClassInfo; -import ch.njol.skript.lang.parser.ParserInstance; +import ch.njol.skript.lang.ReturnHandler; import org.jetbrains.annotations.ApiStatus; import org.skriptlang.skript.lang.script.Script; import org.eclipse.jdt.annotation.Nullable; -import ch.njol.skript.ScriptLoader; import ch.njol.skript.config.SectionNode; import ch.njol.skript.effects.EffReturn; import ch.njol.skript.lang.Trigger; import ch.njol.skript.lang.util.SimpleEvent; import ch.njol.skript.variables.Variables; -public class ScriptFunction extends Function { - +public class ScriptFunction extends Function implements ReturnHandler { + private final Trigger trigger; - + + private boolean returnValueSet; + private T @Nullable [] returnValues; + + /** + * @deprecated use {@link ScriptFunction#ScriptFunction(Signature, SectionNode)} + */ + @Deprecated public ScriptFunction(Signature sign, Script script, SectionNode node) { + this(sign, node); + } + + public ScriptFunction(Signature sign, SectionNode node) { super(sign); - ParserInstance parser = ParserInstance.get(); Functions.currentFunction = this; try { - trigger = new Trigger( - script, - "function " + sign.getName(), - new SimpleEvent(), - trigger -> { - parser.pushReturnData(trigger, getReturnType(), isSingle()); - return ScriptLoader.loadItems(node); - } - ); + trigger = loadReturnableTrigger(node, "function " + sign.getName(), new SimpleEvent()); } finally { Functions.currentFunction = null; - parser.popReturnData(); } trigger.setLineNumber(node.getLine()); } - + // REMIND track possible types of local variables (including undefined variables) (consider functions, commands, and EffChange) - maybe make a general interface for this purpose // REM: use patterns, e.g. {_a%b%} is like "a.*", and thus subsequent {_axyz} may be set and of that type. @Override @@ -76,23 +76,41 @@ public ScriptFunction(Signature sign, Script script, SectionNode node) { trigger.execute(e); ClassInfo returnType = getReturnType(); - return returnType != null ? trigger.getReturnValues(returnType.getC()) : null; + return returnType != null ? returnValues : null; } /** * Should only be called by {@link EffReturn}. - * @deprecated Use {@link ch.njol.skript.lang.TriggerSection#setReturnValues(Object[])} + * @deprecated Use {@link ScriptFunction#returnValues(Object[])} */ @Deprecated @ApiStatus.Internal - public final void setReturnValue(final @Nullable T[] value) { - trigger.setReturnValues(value); + public final void setReturnValue(@Nullable T[] values) { + returnValues(values); } @Override public boolean resetReturnValue() { - trigger.resetReturnValues(); + returnValueSet = false; + returnValues = null; return true; } + @Override + public final void returnValues(T @Nullable [] values) { + assert !returnValueSet; + returnValueSet = true; + this.returnValues = values; + } + + @Override + public final boolean singleReturnValue() { + return isSingle(); + } + + @Override + public final @Nullable ClassInfo returnValueType() { + return getReturnType(); + } + } diff --git a/src/main/java/ch/njol/skript/lang/parser/ParserInstance.java b/src/main/java/ch/njol/skript/lang/parser/ParserInstance.java index 621b0bac958..86d6a209d08 100644 --- a/src/main/java/ch/njol/skript/lang/parser/ParserInstance.java +++ b/src/main/java/ch/njol/skript/lang/parser/ParserInstance.java @@ -20,10 +20,8 @@ import ch.njol.skript.ScriptLoader; import ch.njol.skript.SkriptAPIException; -import ch.njol.skript.classes.ClassInfo; import ch.njol.skript.config.Config; import ch.njol.skript.config.Node; -import ch.njol.skript.effects.EffReturn.ReturnData; import ch.njol.skript.lang.Expression; import ch.njol.skript.lang.SkriptEvent; import ch.njol.skript.lang.SkriptParser; @@ -41,9 +39,7 @@ import java.io.File; import java.util.ArrayList; -import java.util.Deque; import java.util.HashMap; -import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.function.Function; @@ -360,59 +356,6 @@ public Kleenean getHasDelayBefore() { return hasDelayBefore; } - // Return API - - private Deque returnStack = new LinkedList<>(); - - public Deque getReturnStack() { - return returnStack; - } - - public void setReturnStack(Deque returnStack) { - this.returnStack = returnStack; - } - - /** - * Retrieves the current {@link ReturnData} - * @return the return data - */ - @Nullable - public ReturnData getCurrentReturnData() { - return returnStack.peek(); - } - - /** - * Pushes the current trigger onto the return stack. - *
- * Note: After the trigger finished loading, {@link ParserInstance#popReturnData()} MUST be called - * @param section the current trigger - * @param returnType return type - * @param single whether the trigger can return multiple values or not - */ - public void pushReturnData(TriggerSection section, @Nullable ClassInfo returnType, boolean single) { - pushReturnData(new ReturnData(section, returnType, single)); - } - - /** - * Pushes the current trigger onto the return stack. - *
- * Note: After the trigger finished loading, {@link ParserInstance#popReturnData()} MUST be called - * @param data the return data - * @see ParserInstance#popReturnData() - */ - public void pushReturnData(ReturnData data) { - returnStack.push(data); - } - - /** - * Pops the current trigger off the return stack. Should be called after the trigger has finished loading. - * @return the popped return data - * @see ParserInstance#pushReturnData(ReturnData) - */ - public ReturnData popReturnData() { - return returnStack.pop(); - } - // Miscellaneous private final HandlerList handlers = new HandlerList(); From 8cded0eb43c96ceaf442ee24e38d4130b864c007 Mon Sep 17 00:00:00 2001 From: _tud <98935832+UnderscoreTud@users.noreply.github.com> Date: Tue, 14 May 2024 19:55:42 +0300 Subject: [PATCH 13/18] Requested Changes --- .../ch/njol/skript/lang/ReturnHandler.java | 104 +++++++++++------- .../njol/skript/lang/ReturnableTrigger.java | 2 +- 2 files changed, 68 insertions(+), 38 deletions(-) diff --git a/src/main/java/ch/njol/skript/lang/ReturnHandler.java b/src/main/java/ch/njol/skript/lang/ReturnHandler.java index 1441de032a9..e2782d78143 100644 --- a/src/main/java/ch/njol/skript/lang/ReturnHandler.java +++ b/src/main/java/ch/njol/skript/lang/ReturnHandler.java @@ -37,51 +37,62 @@ public interface ReturnHandler { + /** + * Loads the code in the given {@link SectionNode} using the same logic as + * {@link Section#loadCode(SectionNode)} and pushes the section onto the + * return handler stack + *
+ * This method may only be called by a {@link Section} + * @throws SkriptAPIException if this return handler is not a {@link Section} + */ @NonExtendable default void loadReturnableSectionCode(SectionNode node) { - if (!(this instanceof TriggerSection)) + if (!(this instanceof Section)) throw new SkriptAPIException("loadReturnableSectionCode called on a non-section object"); ParserInstance parser = ParserInstance.get(); ReturnHandlerStack stack = parser.getData(ReturnHandlerStack.class); stack.push(this); - TriggerSection section = (TriggerSection) this; - List currentSections = parser.getCurrentSections(); - currentSections.add(section); + Section section = (Section) this; try { - section.setTriggerItems(ScriptLoader.loadItems(node)); + section.loadCode(node); } finally { - currentSections.remove(currentSections.size() - 1); stack.pop(); } } + /** + * Loads the code in the given {@link SectionNode} using the same logic as + * {@link Section#loadCode(SectionNode, String, Class[])} and pushes the section onto the + * return handler stack + *
+ * This method may only be called by a {@link Section} + * @param node the section node + * @param name the name of the event(s) being used + * @param events the event(s) during the section's execution + * @return a returnable trigger containing the loaded section. + * This should be stored and used to run the section one or more times + * @throws SkriptAPIException if this return handler is not a {@link Section} + */ @NonExtendable - default Trigger loadReturnableTrigger(SectionNode node, String name, Class[] events) { + default ReturnableTrigger loadReturnableSectionCode(SectionNode node, String name, Class[] events) { + if (!(this instanceof Section)) + throw new SkriptAPIException("loadReturnableSectionCode called on a non-section object"); ParserInstance parser = ParserInstance.get(); ReturnHandlerStack stack = parser.getData(ReturnHandlerStack.class); - boolean isSection = this instanceof Section; - SkriptEvent skriptEvent = isSection ? new SectionSkriptEvent(name, (Section) this) : new SimpleEvent(); - String previousName = null; - Class[] previousEvents = null; - Structure previousStructure = null; - List previousSections = null; - Kleenean previousDelay = null; - Deque> previousReturnStack = null; - if (isSection) { - previousName = parser.getCurrentEventName(); - previousEvents = parser.getCurrentEvents(); - previousStructure = parser.getCurrentStructure(); - previousSections = parser.getCurrentSections(); - previousDelay = parser.getHasDelayBefore(); - - parser.setCurrentEvent(name, events); - parser.setCurrentStructure(skriptEvent); - parser.setCurrentSections(new ArrayList<>()); - parser.setHasDelayBefore(Kleenean.FALSE); - } + + SkriptEvent skriptEvent = new SectionSkriptEvent(name, (Section) this); + String previousName = parser.getCurrentEventName(); + Class[] previousEvents = parser.getCurrentEvents(); + Structure previousStructure = parser.getCurrentStructure(); + List previousSections = parser.getCurrentSections(); + Kleenean previousDelay = parser.getHasDelayBefore(); + + parser.setCurrentEvent(name, events); + parser.setCurrentStructure(skriptEvent); + parser.setCurrentSections(new ArrayList<>()); + parser.setHasDelayBefore(Kleenean.FALSE); try { - // push handler - return new ReturnableTrigger( + return new ReturnableTrigger<>( this, parser.getCurrentScript(), name, @@ -93,17 +104,25 @@ default Trigger loadReturnableTrigger(SectionNode node, String name, Class + * This is a general method to load a section node without extra logic + * done to the {@link ParserInstance} + * @param node the section node to load + * @param name the name of the trigger + * @param event the {@link SkriptEvent} of the trigger + * @return a returnable trigger containing the loaded section node + */ @NonExtendable - default Trigger loadReturnableTrigger(SectionNode node, String name, SkriptEvent event) { + default ReturnableTrigger loadReturnableTrigger(SectionNode node, String name, SkriptEvent event) { ParserInstance parser = ParserInstance.get(); ReturnHandlerStack stack = parser.getData(ReturnHandlerStack.class); try { @@ -122,10 +141,21 @@ default Trigger loadReturnableTrigger(SectionNode node, String name, SkriptEvent } } + /** + * @param values the values to return + */ void returnValues(T @Nullable [] values); + /** + * @return whether this return handler may accept multiple return values + */ boolean singleReturnValue(); + /** + * The return type of this return handler, or null if it can't + * accept return values in this context (e.g. a function without a return type). + * @return the return type + */ @Nullable ClassInfo returnValueType(); class ReturnHandlerStack extends ParserInstance.Data { @@ -171,5 +201,5 @@ public ReturnHandler pop() { } } - + } diff --git a/src/main/java/ch/njol/skript/lang/ReturnableTrigger.java b/src/main/java/ch/njol/skript/lang/ReturnableTrigger.java index c79e397db95..17f8287816d 100644 --- a/src/main/java/ch/njol/skript/lang/ReturnableTrigger.java +++ b/src/main/java/ch/njol/skript/lang/ReturnableTrigger.java @@ -29,7 +29,7 @@ public class ReturnableTrigger extends Trigger implements ReturnHandler { private final ReturnHandler handler; - + public ReturnableTrigger(ReturnHandler handler, @Nullable Script script, String name, SkriptEvent event, Function, List> loadItems) { super(script, name, event, Collections.emptyList()); this.handler = handler; From bcdc5db88e0a22fe1dde31ec8f7e25c7ff2913bc Mon Sep 17 00:00:00 2001 From: _tud <98935832+UnderscoreTud@users.noreply.github.com> Date: Wed, 15 May 2024 23:01:59 +0300 Subject: [PATCH 14/18] Requested Changes --- src/main/java/ch/njol/skript/effects/EffReturn.java | 8 ++++---- src/main/java/ch/njol/skript/lang/ReturnHandler.java | 7 ++++--- src/main/java/ch/njol/skript/lang/ReturnableTrigger.java | 4 ++-- src/main/java/ch/njol/skript/lang/Section.java | 3 +-- .../java/ch/njol/skript/lang/function/ScriptFunction.java | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/main/java/ch/njol/skript/effects/EffReturn.java b/src/main/java/ch/njol/skript/effects/EffReturn.java index 50b2dc9ccb7..4e4e1d48b4e 100644 --- a/src/main/java/ch/njol/skript/effects/EffReturn.java +++ b/src/main/java/ch/njol/skript/effects/EffReturn.java @@ -77,7 +77,7 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye ClassInfo returnType = handler.returnValueType(); if (returnType == null) { - Skript.error("This trigger doesn't return any value. Please use 'stop' or 'exit' if you want to stop the trigger."); + Skript.error(handler + " doesn't return any value. Please use 'stop' or 'exit' if you want to stop the trigger."); return false; } @@ -86,7 +86,7 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye try { convertedExpr = exprs[0].getConvertedExpression(returnType.getC()); if (convertedExpr == null) { - log.printErrors("This trigger is declared to return " + returnType.getName().withIndefiniteArticle() + ", but " + exprs[0].toString(null, false) + " is not of that type."); + log.printErrors(handler + " is declared to return " + returnType.getName().withIndefiniteArticle() + ", but " + exprs[0].toString(null, false) + " is not of that type."); return false; } log.printLog(); @@ -94,8 +94,8 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye log.stop(); } - if (handler.singleReturnValue() && !convertedExpr.isSingle()) { - Skript.error("This trigger is defined to only return a single " + returnType + ", but this return statement can return multiple values."); + if (handler.isSingleReturnValue() && !convertedExpr.isSingle()) { + Skript.error(handler + " is defined to only return a single " + returnType + ", but this return statement can return multiple values."); return false; } value = convertedExpr; diff --git a/src/main/java/ch/njol/skript/lang/ReturnHandler.java b/src/main/java/ch/njol/skript/lang/ReturnHandler.java index e2782d78143..2b417441f43 100644 --- a/src/main/java/ch/njol/skript/lang/ReturnHandler.java +++ b/src/main/java/ch/njol/skript/lang/ReturnHandler.java @@ -23,7 +23,6 @@ import ch.njol.skript.classes.ClassInfo; import ch.njol.skript.config.SectionNode; import ch.njol.skript.lang.parser.ParserInstance; -import ch.njol.skript.lang.util.SimpleEvent; import ch.njol.util.Kleenean; import org.bukkit.event.Event; import org.eclipse.jdt.annotation.Nullable; @@ -115,7 +114,9 @@ default ReturnableTrigger loadReturnableSectionCode(SectionNode node, String * Loads the code in the given {@link SectionNode} into a {@link ReturnableTrigger}. *
* This is a general method to load a section node without extra logic - * done to the {@link ParserInstance} + * done to the {@link ParserInstance}. + * The calling code is expected to manage the {@code ParserInstance} accordingly, which may vary depending on + * where the code being loaded is located and what state the {@code ParserInstance} is in. * @param node the section node to load * @param name the name of the trigger * @param event the {@link SkriptEvent} of the trigger @@ -149,7 +150,7 @@ default ReturnableTrigger loadReturnableTrigger(SectionNode node, String name /** * @return whether this return handler may accept multiple return values */ - boolean singleReturnValue(); + boolean isSingleReturnValue(); /** * The return type of this return handler, or null if it can't diff --git a/src/main/java/ch/njol/skript/lang/ReturnableTrigger.java b/src/main/java/ch/njol/skript/lang/ReturnableTrigger.java index 17f8287816d..abb0bbe7190 100644 --- a/src/main/java/ch/njol/skript/lang/ReturnableTrigger.java +++ b/src/main/java/ch/njol/skript/lang/ReturnableTrigger.java @@ -42,8 +42,8 @@ public void returnValues(T @Nullable [] values) { } @Override - public boolean singleReturnValue() { - return handler.singleReturnValue(); + public boolean isSingleReturnValue() { + return handler.isSingleReturnValue(); } @Override diff --git a/src/main/java/ch/njol/skript/lang/Section.java b/src/main/java/ch/njol/skript/lang/Section.java index 0a8b3a58f63..9c4122c686e 100644 --- a/src/main/java/ch/njol/skript/lang/Section.java +++ b/src/main/java/ch/njol/skript/lang/Section.java @@ -37,8 +37,7 @@ * A section that can decide what it does with its contents, as code isn't parsed by default. *

* In most cases though, a section should load its code through one of the following loading methods: - * {@link #loadCode(SectionNode)}, {@link #loadCode(SectionNode, String, Class[])}, {@link #loadOptionalCode(SectionNode)}, - * {@link ReturnHandler#loadReturnableSectionCode(SectionNode)} + * {@link #loadCode(SectionNode)}, {@link #loadCode(SectionNode, String, Class[])}, {@link #loadOptionalCode(SectionNode)} *

* Every section must override the {@link TriggerSection#walk(Event)} method. In this method, you can determine whether * the section should run. If you have stored a {@link Trigger} from {@link #loadCode(SectionNode, String, Class[])}, you * should not run it with this event passed in this walk method. diff --git a/src/main/java/ch/njol/skript/lang/function/ScriptFunction.java b/src/main/java/ch/njol/skript/lang/function/ScriptFunction.java index 40a009c9122..30dd8a229f9 100644 --- a/src/main/java/ch/njol/skript/lang/function/ScriptFunction.java +++ b/src/main/java/ch/njol/skript/lang/function/ScriptFunction.java @@ -104,7 +104,7 @@ public final void returnValues(T @Nullable [] values) { } @Override - public final boolean singleReturnValue() { + public final boolean isSingleReturnValue() { return isSingle(); } From a79571bb4d504b539bc72253110a230bcc685046 Mon Sep 17 00:00:00 2001 From: _tud <98935832+UnderscoreTud@users.noreply.github.com> Date: Wed, 15 May 2024 23:32:36 +0300 Subject: [PATCH 15/18] Requested Changes --- src/main/java/ch/njol/skript/lang/ReturnHandler.java | 2 +- src/main/java/ch/njol/skript/lang/ReturnableTrigger.java | 2 +- src/main/java/ch/njol/skript/lang/function/ScriptFunction.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/ch/njol/skript/lang/ReturnHandler.java b/src/main/java/ch/njol/skript/lang/ReturnHandler.java index 2b417441f43..f168574290f 100644 --- a/src/main/java/ch/njol/skript/lang/ReturnHandler.java +++ b/src/main/java/ch/njol/skript/lang/ReturnHandler.java @@ -157,7 +157,7 @@ default ReturnableTrigger loadReturnableTrigger(SectionNode node, String name * accept return values in this context (e.g. a function without a return type). * @return the return type */ - @Nullable ClassInfo returnValueType(); + @Nullable ClassInfo returnValueType(); class ReturnHandlerStack extends ParserInstance.Data { diff --git a/src/main/java/ch/njol/skript/lang/ReturnableTrigger.java b/src/main/java/ch/njol/skript/lang/ReturnableTrigger.java index abb0bbe7190..7457411adeb 100644 --- a/src/main/java/ch/njol/skript/lang/ReturnableTrigger.java +++ b/src/main/java/ch/njol/skript/lang/ReturnableTrigger.java @@ -47,7 +47,7 @@ public boolean isSingleReturnValue() { } @Override - public @Nullable ClassInfo returnValueType() { + public @Nullable ClassInfo returnValueType() { return handler.returnValueType(); } diff --git a/src/main/java/ch/njol/skript/lang/function/ScriptFunction.java b/src/main/java/ch/njol/skript/lang/function/ScriptFunction.java index 30dd8a229f9..85fa91718de 100644 --- a/src/main/java/ch/njol/skript/lang/function/ScriptFunction.java +++ b/src/main/java/ch/njol/skript/lang/function/ScriptFunction.java @@ -109,7 +109,7 @@ public final boolean isSingleReturnValue() { } @Override - public final @Nullable ClassInfo returnValueType() { + public final @Nullable ClassInfo returnValueType() { return getReturnType(); } From 5d75baf752bba0146f540367c5fdc0ac2c6a4ca1 Mon Sep 17 00:00:00 2001 From: _tud <98935832+UnderscoreTud@users.noreply.github.com> Date: Sat, 25 May 2024 04:19:34 +0300 Subject: [PATCH 16/18] Add tests --- .../skript/test/runner/SecReturnable.java | 114 ++++++++++++++++++ src/test/skript/tests/misc/returns.sk | 23 ++++ 2 files changed, 137 insertions(+) create mode 100644 src/main/java/ch/njol/skript/test/runner/SecReturnable.java create mode 100644 src/test/skript/tests/misc/returns.sk diff --git a/src/main/java/ch/njol/skript/test/runner/SecReturnable.java b/src/main/java/ch/njol/skript/test/runner/SecReturnable.java new file mode 100644 index 00000000000..bead1d358df --- /dev/null +++ b/src/main/java/ch/njol/skript/test/runner/SecReturnable.java @@ -0,0 +1,114 @@ +/** + * This file is part of Skript. + * + * Skript is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Skript is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Skript. If not, see . + * + * Copyright Peter Güttinger, SkriptLang team and contributors + */ +package ch.njol.skript.test.runner; + +import ch.njol.skript.Skript; +import ch.njol.skript.classes.ClassInfo; +import ch.njol.skript.config.SectionNode; +import ch.njol.skript.doc.NoDoc; +import ch.njol.skript.lang.*; +import ch.njol.skript.lang.SkriptParser.ParseResult; +import ch.njol.skript.lang.util.SimpleExpression; +import ch.njol.util.Kleenean; +import org.bukkit.event.Event; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +@NoDoc +public class SecReturnable extends Section implements ReturnHandler { + + static { + Skript.registerSection(SecReturnable.class, "returnable [:plural] %*classinfo% section"); + } + + private ClassInfo returnValueType; + private boolean singleReturnValue; + private static Object @Nullable [] returnedValues; + + @Override + public boolean init(Expression[] expressions, int matchedPattern, Kleenean isDelayed, ParseResult parseResult, SectionNode sectionNode, List triggerItems) { + returnValueType = ((Literal>) expressions[0]).getSingle(); + singleReturnValue = !parseResult.hasTag("plural"); + loadReturnableSectionCode(sectionNode); + return true; + } + + @Override + protected @Nullable TriggerItem walk(Event event) { + return walk(event, true); + } + + @Override + public void returnValues(Object @Nullable [] values) { + returnedValues = values; + } + + @Override + public boolean isSingleReturnValue() { + return singleReturnValue; + } + + @Override + public @Nullable ClassInfo returnValueType() { + return returnValueType; + } + + @Override + public String toString(@Nullable Event event, boolean debug) { + return "section"; + } + + @NoDoc + public static class ExprLastReturnValues extends SimpleExpression { + + static { + Skript.registerExpression(ExprLastReturnValues.class, Object.class, ExpressionType.SIMPLE, "[the] last return[ed] value[s]"); + } + + @Override + public boolean init(Expression[] expressions, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { + return true; + } + + @Override + public @Nullable Object[] get(Event event) { + Object[] returnedValues = SecReturnable.returnedValues; + SecReturnable.returnedValues = null; + return returnedValues; + } + + @Override + public boolean isSingle() { + return false; + } + + @Override + public Class getReturnType() { + return Object.class; + } + + @Override + public String toString(@Nullable Event event, boolean debug) { + return "last returned values"; + } + + } + +} diff --git a/src/test/skript/tests/misc/returns.sk b/src/test/skript/tests/misc/returns.sk new file mode 100644 index 00000000000..56e2937f7c5 --- /dev/null +++ b/src/test/skript/tests/misc/returns.sk @@ -0,0 +1,23 @@ +test "returns": + returnable string section: + return "hello" + assert last return value is "hello" with "failed single return value" + + returnable plural number section: + return (3 and 5) + assert last return values is 3 and 5 with "failed multiple return values" + + returnable integer section: + return 2.5 + assert last return value is 2 with "failed converted return value" + +test "returns (parsing)": + parse: + returnable string section: + return 1 + assert last parse logs is set with "skript shouldn't be able to return a different type" + + parse: + returnable string section: + return ("foo" and "bar") + assert last parse logs is set with "skript shouldn't be able to return multiple values for a single return trigger" From c6c98a99d3788ee53a1b6d81dfa87736fd1c282b Mon Sep 17 00:00:00 2001 From: _tud <98935832+UnderscoreTud@users.noreply.github.com> Date: Sat, 1 Jun 2024 04:49:49 +0300 Subject: [PATCH 17/18] Use parser instance backups --- .../ch/njol/skript/lang/ReturnHandler.java | 24 +++++-------------- 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/src/main/java/ch/njol/skript/lang/ReturnHandler.java b/src/main/java/ch/njol/skript/lang/ReturnHandler.java index f168574290f..93e65045683 100644 --- a/src/main/java/ch/njol/skript/lang/ReturnHandler.java +++ b/src/main/java/ch/njol/skript/lang/ReturnHandler.java @@ -23,16 +23,12 @@ import ch.njol.skript.classes.ClassInfo; import ch.njol.skript.config.SectionNode; import ch.njol.skript.lang.parser.ParserInstance; -import ch.njol.util.Kleenean; import org.bukkit.event.Event; import org.eclipse.jdt.annotation.Nullable; import org.jetbrains.annotations.ApiStatus.NonExtendable; -import org.skriptlang.skript.lang.structure.Structure; -import java.util.ArrayList; import java.util.Deque; import java.util.LinkedList; -import java.util.List; public interface ReturnHandler { @@ -77,19 +73,14 @@ default ReturnableTrigger loadReturnableSectionCode(SectionNode node, String if (!(this instanceof Section)) throw new SkriptAPIException("loadReturnableSectionCode called on a non-section object"); ParserInstance parser = ParserInstance.get(); - ReturnHandlerStack stack = parser.getData(ReturnHandlerStack.class); - - SkriptEvent skriptEvent = new SectionSkriptEvent(name, (Section) this); - String previousName = parser.getCurrentEventName(); - Class[] previousEvents = parser.getCurrentEvents(); - Structure previousStructure = parser.getCurrentStructure(); - List previousSections = parser.getCurrentSections(); - Kleenean previousDelay = parser.getHasDelayBefore(); + ParserInstance.Backup parserBackup = parser.backup(); + parser.reset(); parser.setCurrentEvent(name, events); + SkriptEvent skriptEvent = new SectionSkriptEvent(name, (Section) this); parser.setCurrentStructure(skriptEvent); - parser.setCurrentSections(new ArrayList<>()); - parser.setHasDelayBefore(Kleenean.FALSE); + ReturnHandlerStack stack = parser.getData(ReturnHandlerStack.class); + try { return new ReturnableTrigger<>( this, @@ -103,10 +94,7 @@ default ReturnableTrigger loadReturnableSectionCode(SectionNode node, String ); } finally { stack.pop(); - parser.setCurrentEvent(previousName, previousEvents); - parser.setCurrentStructure(previousStructure); - parser.setCurrentSections(previousSections); - parser.setHasDelayBefore(previousDelay); + parser.restoreBackup(parserBackup); } } From bc31aaa40575cdbfa5e11fad088168467cc4fd29 Mon Sep 17 00:00:00 2001 From: _tud <98935832+UnderscoreTud@users.noreply.github.com> Date: Fri, 14 Jun 2024 23:12:34 +0300 Subject: [PATCH 18/18] Make ReturnHandler#returnValueType return a Class object rather than a ClassInfo object --- src/main/java/ch/njol/skript/effects/EffReturn.java | 8 +++++--- src/main/java/ch/njol/skript/lang/ReturnHandler.java | 6 +++--- src/main/java/ch/njol/skript/lang/ReturnableTrigger.java | 3 +-- .../java/ch/njol/skript/lang/function/ScriptFunction.java | 4 ++-- .../java/ch/njol/skript/test/runner/SecReturnable.java | 6 +++--- 5 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/main/java/ch/njol/skript/effects/EffReturn.java b/src/main/java/ch/njol/skript/effects/EffReturn.java index 4e4e1d48b4e..21ce3d4d475 100644 --- a/src/main/java/ch/njol/skript/effects/EffReturn.java +++ b/src/main/java/ch/njol/skript/effects/EffReturn.java @@ -35,6 +35,7 @@ import ch.njol.skript.lang.parser.ParserInstance; import ch.njol.skript.log.RetainingLogHandler; import ch.njol.skript.log.SkriptLogger; +import ch.njol.skript.registrations.Classes; import ch.njol.util.Kleenean; import org.bukkit.event.Event; import org.eclipse.jdt.annotation.Nullable; @@ -75,7 +76,7 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye return false; } - ClassInfo returnType = handler.returnValueType(); + Class returnType = handler.returnValueType(); if (returnType == null) { Skript.error(handler + " doesn't return any value. Please use 'stop' or 'exit' if you want to stop the trigger."); return false; @@ -84,9 +85,10 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye RetainingLogHandler log = SkriptLogger.startRetainingLog(); Expression convertedExpr; try { - convertedExpr = exprs[0].getConvertedExpression(returnType.getC()); + convertedExpr = exprs[0].getConvertedExpression(returnType); if (convertedExpr == null) { - log.printErrors(handler + " is declared to return " + returnType.getName().withIndefiniteArticle() + ", but " + exprs[0].toString(null, false) + " is not of that type."); + String typeName = Classes.getSuperClassInfo(returnType).getName().withIndefiniteArticle(); + log.printErrors(handler + " is declared to return " + typeName + ", but " + exprs[0].toString(null, false) + " is not of that type."); return false; } log.printLog(); diff --git a/src/main/java/ch/njol/skript/lang/ReturnHandler.java b/src/main/java/ch/njol/skript/lang/ReturnHandler.java index 93e65045683..f908fc68c51 100644 --- a/src/main/java/ch/njol/skript/lang/ReturnHandler.java +++ b/src/main/java/ch/njol/skript/lang/ReturnHandler.java @@ -20,12 +20,11 @@ import ch.njol.skript.ScriptLoader; import ch.njol.skript.SkriptAPIException; -import ch.njol.skript.classes.ClassInfo; import ch.njol.skript.config.SectionNode; import ch.njol.skript.lang.parser.ParserInstance; import org.bukkit.event.Event; -import org.eclipse.jdt.annotation.Nullable; import org.jetbrains.annotations.ApiStatus.NonExtendable; +import org.jetbrains.annotations.Nullable; import java.util.Deque; import java.util.LinkedList; @@ -143,9 +142,10 @@ default ReturnableTrigger loadReturnableTrigger(SectionNode node, String name /** * The return type of this return handler, or null if it can't * accept return values in this context (e.g. a function without a return type). + * * @return the return type */ - @Nullable ClassInfo returnValueType(); + @Nullable Class returnValueType(); class ReturnHandlerStack extends ParserInstance.Data { diff --git a/src/main/java/ch/njol/skript/lang/ReturnableTrigger.java b/src/main/java/ch/njol/skript/lang/ReturnableTrigger.java index 7457411adeb..51c025ef70c 100644 --- a/src/main/java/ch/njol/skript/lang/ReturnableTrigger.java +++ b/src/main/java/ch/njol/skript/lang/ReturnableTrigger.java @@ -18,7 +18,6 @@ */ package ch.njol.skript.lang; -import ch.njol.skript.classes.ClassInfo; import org.eclipse.jdt.annotation.Nullable; import org.skriptlang.skript.lang.script.Script; @@ -47,7 +46,7 @@ public boolean isSingleReturnValue() { } @Override - public @Nullable ClassInfo returnValueType() { + public @Nullable Class returnValueType() { return handler.returnValueType(); } diff --git a/src/main/java/ch/njol/skript/lang/function/ScriptFunction.java b/src/main/java/ch/njol/skript/lang/function/ScriptFunction.java index 85fa91718de..d3a1e85117c 100644 --- a/src/main/java/ch/njol/skript/lang/function/ScriptFunction.java +++ b/src/main/java/ch/njol/skript/lang/function/ScriptFunction.java @@ -109,8 +109,8 @@ public final boolean isSingleReturnValue() { } @Override - public final @Nullable ClassInfo returnValueType() { - return getReturnType(); + public final @Nullable Class returnValueType() { + return getReturnType() != null ? getReturnType().getC() : null; } } diff --git a/src/main/java/ch/njol/skript/test/runner/SecReturnable.java b/src/main/java/ch/njol/skript/test/runner/SecReturnable.java index bead1d358df..436b6d282fb 100644 --- a/src/main/java/ch/njol/skript/test/runner/SecReturnable.java +++ b/src/main/java/ch/njol/skript/test/runner/SecReturnable.java @@ -66,13 +66,13 @@ public boolean isSingleReturnValue() { } @Override - public @Nullable ClassInfo returnValueType() { - return returnValueType; + public @Nullable Class returnValueType() { + return returnValueType.getC(); } @Override public String toString(@Nullable Event event, boolean debug) { - return "section"; + return "returnable " + (singleReturnValue ? "" : "plural ") + returnValueType.toString(event, debug) + " section"; } @NoDoc