Skip to content

Commit

Permalink
🚀 Add loop-counter & Improve loops (#4595)
Browse files Browse the repository at this point in the history
  • Loading branch information
AyhamAl-Ali authored Jun 26, 2023
1 parent ce1888c commit 9d06f02
Show file tree
Hide file tree
Showing 9 changed files with 359 additions and 140 deletions.
48 changes: 27 additions & 21 deletions src/main/java/ch/njol/skript/effects/EffContinue.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,25 +25,33 @@
import ch.njol.skript.doc.Since;
import ch.njol.skript.lang.Effect;
import ch.njol.skript.lang.Expression;
import ch.njol.skript.lang.LoopSection;
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.sections.SecLoop;
import ch.njol.skript.sections.SecWhile;
import ch.njol.util.Kleenean;
import org.bukkit.event.Event;
import org.eclipse.jdt.annotation.Nullable;

import java.util.List;
import java.util.stream.Collectors;

@Name("Continue")
@Description("Skips the value currently being looped, moving on to the next value if it exists.")
@Examples({"loop all players:",
@Description("Immediately moves the (while) loop on to the next iteration.")
@Examples({
"# Broadcast online moderators",
"loop all players:",
"\tif loop-value does not have permission \"moderator\":",
"\t\tcontinue # filter out non moderators",
"\tbroadcast \"%loop-player% is a moderator!\" # Only moderators get broadcast"})
"\t\tcontinue # filter out non moderators",
"\tbroadcast \"%loop-player% is a moderator!\" # Only moderators get broadcast",
" ",
"# Game starting counter",
"set {_counter} to 11",
"while {_counter} > 0:",
"\tremove 1 from {_counter}",
"\twait a second",
"\tif {_counter} != 1, 2, 3, 5 or 10:",
"\t\tcontinue # only print when counter is 1, 2, 3, 5 or 10",
"\tbroadcast \"Game starting in %{_counter}% second(s)\"",
})
@Since("2.2-dev37, 2.7 (while loops)")
public class EffContinue extends Effect {

Expand All @@ -52,36 +60,34 @@ public class EffContinue extends Effect {
}

@SuppressWarnings("NotNullFieldNotInitialized")
private TriggerSection section;
private LoopSection loop;

@Override
public boolean init(Expression<?>[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) {
List<TriggerSection> currentSections = ParserInstance.get().getCurrentSections().stream()
.filter(s -> s instanceof SecLoop || s instanceof SecWhile)
.collect(Collectors.toList());
List<LoopSection> currentLoops = getParser().getCurrentSections(LoopSection.class);

if (currentSections.isEmpty()) {
Skript.error("Continue may only be used in while or loops");
if (currentLoops.isEmpty()) {
Skript.error("The 'continue' effect may only be used in while and regular loops");
return false;
}

section = currentSections.get(currentSections.size() - 1);
loop = currentLoops.get(currentLoops.size() - 1);
return true;
}

@Override
protected void execute(Event e) {
protected void execute(Event event) {
throw new UnsupportedOperationException();
}

@Nullable
@Override
protected TriggerItem walk(Event e) {
return section;
@Nullable
protected TriggerItem walk(Event event) {
return loop;
}

@Override
public String toString(@Nullable Event e, boolean debug) {
public String toString(@Nullable Event event, boolean debug) {
return "continue";
}

Expand Down
72 changes: 35 additions & 37 deletions src/main/java/ch/njol/skript/effects/EffExit.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,50 +25,51 @@
import ch.njol.skript.doc.Since;
import ch.njol.skript.lang.Effect;
import ch.njol.skript.lang.Expression;
import ch.njol.skript.lang.LoopSection;
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.ErrorQuality;
import ch.njol.skript.sections.SecConditional;
import ch.njol.skript.sections.SecLoop;
import ch.njol.skript.sections.SecWhile;
import ch.njol.util.Kleenean;
import org.bukkit.event.Event;
import org.eclipse.jdt.annotation.Nullable;

import java.util.List;

/**
* @author Peter Güttinger
*/
@Name("Exit")
@Description("Exits a given amount of loops and conditionals, or the entire trigger.")
@Examples({"if player has any ore:",
" stop",
"message \"%player% has no ores!\"",
"loop blocks above the player:",
" loop-block is not air:",
" exit 2 sections",
" set loop-block to water"})
@Examples({
"if player has any ore:",
"\tstop",
"message \"%player% has no ores!\"",
"loop blocks above the player:",
"\tloop-block is not air:",
"\t\texit 2 sections",
"\tset loop-block to water"
})
@Since("<i>unknown</i> (before 2.1)")
public class EffExit extends Effect { // TODO [code style] warn user about code after a stop effect

static {
Skript.registerEffect(EffExit.class,
"(exit|stop) [trigger]",
"(exit|stop) [(1|a|the|this)] (section|1¦loop|2¦conditional)",
"(exit|stop) <\\d+> (section|1¦loop|2¦conditional)s",
"(exit|stop) all (section|1¦loop|2¦conditional)s");
"(exit|stop) [(1|a|the|this)] (section|1:loop|2:conditional)",
"(exit|stop) <\\d+> (section|1:loop|2:conditional)s",
"(exit|stop) all (section|1:loop|2:conditional)s");
}

private int breakLevels;

private final static int EVERYTHING = 0, LOOPS = 1, CONDITIONALS = 2;
private final static String[] names = {"sections", "loops", "conditionals"};
private static final int EVERYTHING = 0;
private static final int LOOPS = 1;
private static final int CONDITIONALS = 2;
private static final String[] names = {"sections", "loops", "conditionals"};
private int type;

@Override
public boolean init(final Expression<?>[] exprs, final int matchedPattern, final Kleenean isDelayed, final ParseResult parser) {
public boolean init(Expression<?>[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parser) {
switch (matchedPattern) {
case 0:
breakLevels = getParser().getCurrentSections().size() + 1;
Expand Down Expand Up @@ -102,44 +103,41 @@ private static int numLevels(int type) {
List<TriggerSection> currentSections = ParserInstance.get().getCurrentSections();
if (type == EVERYTHING)
return currentSections.size();
int r = 0;
for (TriggerSection s : currentSections) {
if (type == CONDITIONALS ? s instanceof SecConditional : s instanceof SecLoop || s instanceof SecWhile)
r++;
int level = 0;
for (TriggerSection section : currentSections) {
if (type == CONDITIONALS ? section instanceof SecConditional : section instanceof LoopSection)
level++;
}
return r;
return level;
}

@Override
@Nullable
protected TriggerItem walk(final Event e) {
debug(e, false);
TriggerItem n = this;
protected TriggerItem walk(Event event) {
debug(event, false);
TriggerItem node = this;
for (int i = breakLevels; i > 0;) {
n = n.getParent();
if (n == null) {
node = node.getParent();
if (node == null) {
assert false : this;
return null;
}
if (n instanceof SecLoop) {
((SecLoop) n).exit(e);
} else if (n instanceof SecWhile) {
((SecWhile) n).reset();
}
if (node instanceof LoopSection)
((LoopSection) node).exit(event);

if (type == EVERYTHING || type == CONDITIONALS && n instanceof SecConditional || type == LOOPS && (n instanceof SecLoop || n instanceof SecWhile))
if (type == EVERYTHING || type == CONDITIONALS && node instanceof SecConditional || type == LOOPS && (node instanceof LoopSection))
i--;
}
return n instanceof SecLoop ? ((SecLoop) n).getActualNext() : n instanceof SecWhile ? ((SecWhile) n).getActualNext() : n.getNext();
return node instanceof LoopSection ? ((LoopSection) node).getActualNext() : node.getNext();
}

@Override
protected void execute(final Event e) {
protected void execute(Event event) {
assert false;
}

@Override
public String toString(final @Nullable Event e, final boolean debug) {
public String toString(@Nullable Event event, boolean debug) {
return "stop " + breakLevels + " " + names[type];
}

Expand Down
56 changes: 26 additions & 30 deletions src/main/java/ch/njol/skript/effects/EffReturn.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import ch.njol.skript.doc.Since;
import ch.njol.skript.lang.Effect;
import ch.njol.skript.lang.Expression;
import ch.njol.skript.lang.LoopSection;
import ch.njol.skript.lang.SkriptParser.ParseResult;
import ch.njol.skript.lang.TriggerItem;
import ch.njol.skript.lang.TriggerSection;
Expand All @@ -34,19 +35,16 @@
import ch.njol.skript.lang.function.ScriptFunction;
import ch.njol.skript.log.RetainingLogHandler;
import ch.njol.skript.log.SkriptLogger;
import ch.njol.skript.sections.SecLoop;
import ch.njol.skript.sections.SecWhile;
import ch.njol.util.Kleenean;
import org.bukkit.event.Event;
import org.eclipse.jdt.annotation.Nullable;

/**
* @author Peter Güttinger
*/
@Name("Return")
@Description("Makes a function return a value")
@Examples({"function double(i: number) :: number:",
" return 2 * {_i}"})
@Examples({
"function double(i: number) :: number:",
"\treturn 2 * {_i}"
})
@Since("2.2")
public class EffReturn extends Effect {

Expand Down Expand Up @@ -75,66 +73,64 @@ public boolean init(Expression<?>[] exprs, int matchedPattern, Kleenean isDelaye
}

function = f;
ClassInfo<?> rt = function.getReturnType();
if (rt == null) {
ClassInfo<?> returnType = function.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.");
return false;
}

RetainingLogHandler log = SkriptLogger.startRetainingLog();
Expression<?> v;
Expression<?> convertedExpr;
try {
v = exprs[0].getConvertedExpression(rt.getC());
if (v == null) {
log.printErrors("This function is declared to return " + rt.getName().withIndefiniteArticle() + ", but " + exprs[0].toString(null, false) + " is not of that type.");
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.");
return false;
}
log.printLog();
} finally {
log.stop();
}

if (f.isSingle() && !v.isSingle()) {
Skript.error("This function is defined to only return a single " + rt.toString() + ", but this return statement can return multiple values.");
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.");
return false;
}
value = v;
value = convertedExpr;

return true;
}

@SuppressWarnings({"unchecked", "rawtypes"})
@Override
@Nullable
protected TriggerItem walk(final Event e) {
debug(e, false);
if (e instanceof FunctionEvent) {
((ScriptFunction) function).setReturnValue(value.getArray(e));
@SuppressWarnings({"unchecked", "rawtypes"})
protected TriggerItem walk(Event event) {
debug(event, false);
if (event instanceof FunctionEvent) {
((ScriptFunction) function).setReturnValue(value.getArray(event));
} else {
assert false : e;
assert false : event;
}

TriggerSection parent = getParent();
while (parent != null) {
if (parent instanceof SecLoop) {
((SecLoop) parent).exit(e);
} else if (parent instanceof SecWhile) {
((SecWhile) parent).reset();
}
if (parent instanceof LoopSection)
((LoopSection) parent).exit(event);

parent = parent.getParent();
}

return null;
}

@Override
protected void execute(Event e) {
protected void execute(Event event) {
assert false;
}

@Override
public String toString(@Nullable Event e, boolean debug) {
return "return " + value.toString(e, debug);
public String toString(@Nullable Event event, boolean debug) {
return "return " + value.toString(event, debug);
}

}
Loading

0 comments on commit 9d06f02

Please sign in to comment.