Skip to content

Feature/default expressions #130

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 8 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ public boolean init(Expression<?>[] expressions, int matchedPattern, ParseContex
} else if (!changed.acceptsChange(mode, changeWith)) {
var type = TypeManager.getByClassExact(changeWith.getReturnType());
assert type.isPresent();
String changeTypeName = type.get().withIndefiniteArticle(!changeWith.isSingle());
String changeTypeName = type.get().withIndefiniteArticle(changeWith.isSingle());
switch (mode) {
case SET:
logger.error(changedString + " cannot be set to " + changeTypeName, ErrorType.SEMANTIC_ERROR);
Expand Down
32 changes: 26 additions & 6 deletions src/main/java/io/github/syst3ms/skriptparser/lang/Expression.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@

import io.github.syst3ms.skriptparser.expressions.ExprLoopValue;
import io.github.syst3ms.skriptparser.lang.base.ConvertedExpression;
import io.github.syst3ms.skriptparser.parsing.ParseContext;
import io.github.syst3ms.skriptparser.parsing.ParserState;
import io.github.syst3ms.skriptparser.parsing.SkriptParserException;
import io.github.syst3ms.skriptparser.parsing.SkriptRuntimeException;
import io.github.syst3ms.skriptparser.registration.SyntaxManager;
import io.github.syst3ms.skriptparser.sections.SecLoop;
import io.github.syst3ms.skriptparser.types.TypeManager;
import io.github.syst3ms.skriptparser.types.changers.ChangeMode;
import io.github.syst3ms.skriptparser.types.changers.Changer;
import io.github.syst3ms.skriptparser.types.conversions.Converters;

import java.util.ArrayList;
Expand Down Expand Up @@ -74,7 +77,7 @@ default boolean isSingle() {
}

/**
* @return the return type of this expression. By default, this is defined on registration, but, like {@linkplain #isSingle()}, can be overriden.
* @return the return type of this expression. By default, this is defined on registration, but, like {@linkplain #isSingle()}, can be overridden.
*/
default Class<? extends T> getReturnType() {
return SyntaxManager.getExpressionExact(this)
Expand All @@ -93,7 +96,10 @@ default Class<? extends T> getReturnType() {
* {@link ChangeMode#DELETE} or {@link ChangeMode#RESET}, then an empty array should be returned.
*/
default Optional<Class<?>[]> acceptsChange(ChangeMode mode) {
return Optional.empty();
return TypeManager.getByClassExact(getReturnType())
.orElseThrow(() -> new SkriptParserException("Couldn't find a type corresponding to the class '" + getReturnType().getName() + "'"))
.getDefaultChanger()
.map(ch -> ch.acceptsChange(mode));
}

/**
Expand Down Expand Up @@ -128,12 +134,18 @@ default boolean acceptsChange(ChangeMode mode, Class<?> needle, boolean isSingle
}

/**
* Changes this expression with the given values according to the given mode
* Changes this expression with the given values according to the given mode.
* @param ctx the event
* @param changeMode the mode of change
* @param mode the mode of change
* @param changeWith the values to change this Expression with
*/
default void change(TriggerContext ctx, ChangeMode changeMode, Object[] changeWith) { /* Nothing */ }
@SuppressWarnings("unchecked")
default void change(TriggerContext ctx, ChangeMode mode, Object[] changeWith) {
TypeManager.getByClassExact(getReturnType())
.orElseThrow(() -> new SkriptParserException("Couldn't find a type corresponding to the class '" + getReturnType().getName() + "'"))
.getDefaultChanger()
.ifPresent(ch -> ((Changer<T>) ch).change(getArray(ctx), mode, changeWith));
}

/**
* @param ctx the event
Expand All @@ -152,7 +164,7 @@ default Stream<? extends T> stream(TriggerContext ctx) {
}

/**
* Converts this expression from it's current type ({@link T}) to another type, using
* Converts this expression from its current type ({@link T}) to another type, using
* {@linkplain Converters converters}.
* @param to the class of the type to convert this Expression to
* @param <C> the type to convert this Expression to
Expand Down Expand Up @@ -232,6 +244,14 @@ static <T> boolean check(T[] all, Predicate<? super T> predicate, boolean negate
return negated != and;
}

static boolean initialize(Expression<?> expression, Expression<?>[] expressions, int matchedPattern, ParseContext parseContext) {
try {
return expression.init(expressions, matchedPattern, parseContext);
} catch (Exception ignored) {
return true;
}
}

@SuppressWarnings("unchecked")
static <S extends CodeSection> List<? extends S> getMatchingSections(ParserState parserState,
Class<? extends S> sectionClass) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
package io.github.syst3ms.skriptparser.lang;

import io.github.syst3ms.skriptparser.parsing.ParseContext;
import io.github.syst3ms.skriptparser.types.TypeManager;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.Nullable;

import java.util.function.Function;

Expand All @@ -15,18 +13,13 @@ public class SimpleExpression<T> implements Expression<T> {
private final Class<? extends T> returnType;
private final boolean isSingle;
private final Function<TriggerContext, T[]> function;
@Nullable
private final String representation;
private final String toString;

public SimpleExpression(Class<? extends T> returnType, boolean isSingle, Function<TriggerContext, T[]> function) {
this(returnType, isSingle, function, null);
}

public SimpleExpression(Class<? extends T> returnType, boolean isSingle, Function<TriggerContext, T[]> function, @Nullable String representation) {
public SimpleExpression(Class<? extends T> returnType, boolean isSingle, Function<TriggerContext, T[]> function, String toString) {
this.returnType = returnType;
this.isSingle = isSingle;
this.function = function;
this.representation = representation;
this.toString = toString;
}

@Override
Expand All @@ -51,11 +44,6 @@ public Class<? extends T> getReturnType() {

@Override
public String toString(TriggerContext ctx, boolean debug) {
// If the representation is not given, the default TypeManager#toString will be used to convert each value separately
if (representation == null) {
return TypeManager.toString((Object[]) function.apply(ctx));
} else {
return representation;
}
return toString;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,15 @@
public class SimpleLiteral<T> implements Literal<T> {
private final T[] values;
private boolean isAndList = true;
private Class<T> returnType;
private final Class<T> returnType;

public SimpleLiteral(T[] values) {
this.values = values;
public SimpleLiteral(T... values) {
this((Class<T>) values.getClass().getComponentType(), values);
}

@SafeVarargs
public SimpleLiteral(Class<T> c, T... values) {
public SimpleLiteral(Class<T> returnType, T... values) {
this.returnType = returnType;
this.values = Arrays.copyOf(values, values.length);
}

Expand Down Expand Up @@ -60,11 +61,7 @@ public boolean isSingle() {
}

public Class<? extends T> getReturnType() {
if (returnType != null) {
return returnType;
} else {
return returnType = (Class<T>) values.getClass().getComponentType();
}
return returnType;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ public void change(TriggerContext ctx, ChangeMode mode, Object[] changeWith) thr
if (changer.map(ch -> ch.acceptsChange(ChangeMode.RESET)).isPresent()) {
var one = (Object[]) Array.newInstance(o.getClass(), 1);
one[0] = o;
changer.ifPresent(ch -> ((Changer<Object>) ch).change(one, new Object[0], ChangeMode.RESET));
changer.ifPresent(ch -> ((Changer<Object>) ch).change(one, ChangeMode.RESET, new Object[0]));
}
}
break;
Expand Down Expand Up @@ -291,7 +291,7 @@ public void change(TriggerContext ctx, ChangeMode mode, Object[] changeWith) thr
if (d2 != null)
l.add(d2);
}
((Changer<Object>) changer.get()).change(one, l.toArray(), mode);
((Changer<Object>) changer.get()).change(one, mode, l.toArray());
}
break;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package io.github.syst3ms.skriptparser.lang.base;

import io.github.syst3ms.skriptparser.lang.Expression;
import io.github.syst3ms.skriptparser.lang.TriggerContext;
import io.github.syst3ms.skriptparser.log.ErrorType;
import io.github.syst3ms.skriptparser.parsing.ParseContext;
import io.github.syst3ms.skriptparser.parsing.SkriptRuntimeException;
import io.github.syst3ms.skriptparser.registration.context.ContextValue;
import io.github.syst3ms.skriptparser.registration.context.ContextValues;
import io.github.syst3ms.skriptparser.types.PatternType;
import io.github.syst3ms.skriptparser.types.Type;
import io.github.syst3ms.skriptparser.types.TypeManager;

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

/**
* One can use this as a utility class for a {@linkplain Type#getDefaultExpression() default expression}.
* This expression class holds a reference to a specific {@link ContextValue} based on the required
* return type and whether or not the result must be a single value. A runtime exception will be thrown
* if such context value does not exist.
* <br>
* Note that the {@linkplain ContextValue.State state} of the referenced context value must be 'present'.
* @param <T> the Expression's type
* @author Mwexim
*/
public class EventExpression<T> implements Expression<T> {
private final PatternType<? extends T> returnType;
private final List<ContextValue<? extends TriggerContext, T>> contextValues = new ArrayList<>();

public EventExpression(Class<? extends T> returnType, boolean isSingle) {
this.returnType = new PatternType<>(TypeManager.getByClassExact(returnType).orElseThrow(IllegalArgumentException::new), isSingle);
}

@Override
public boolean init(Expression<?>[] expressions, int matchedPattern, ParseContext parseContext) {
parseContext.getParserState().getCurrentContexts().stream()
.map(ctx -> ContextValues.getContextValues(ctx).stream()
.filter(info -> returnType.getType().getTypeClass().isAssignableFrom(info.getReturnType().getType().getTypeClass()))
.filter(info -> !returnType.isSingle() || info.getReturnType().isSingle())
.filter(info -> info.getState() == ContextValue.State.PRESENT)
.collect(Collectors.toList()))
.map(List.class::cast)
.forEach(contextValues::addAll);
if (contextValues.size() == 0) {
parseContext.getLogger().error("Couldn't find "
+ returnType.getType().withIndefiniteArticle(returnType.isSingle())
+ " as default expression in this event",
ErrorType.NO_MATCH);
return false;
}
return true;
}

@SuppressWarnings("unchecked")
@Override
public T[] getValues(TriggerContext ctx) {
var info = (ContextValue<TriggerContext, T>) contextValues.stream()
.filter(val -> val.getContext().isAssignableFrom(ctx.getClass()))
.findFirst()
.orElseThrow(() -> new SkriptRuntimeException("Couldn't find any context value corresponding to the class '" + ctx.getClass().getName() + "'"));
return info.getFunction().apply(ctx);
}

@Override
public boolean isSingle() {
return returnType.isSingle();
}

@Override
public Class<? extends T> getReturnType() {
return returnType.getType().getTypeClass();
}

@SuppressWarnings("unchecked")
@Override
public String toString(TriggerContext ctx, boolean debug) {
var info = (ContextValue<TriggerContext, T>) contextValues.stream()
.filter(val -> val.getContext().isAssignableFrom(ctx.getClass()))
.findFirst().orElse(null);
if (info != null) {
return new String[] {"past ", "", "future "}[info.getState().ordinal()]
+ (info.getUsage().isCorrect(true) ? "" : "context-")
+ info.getReturnType().getType().withIndefiniteArticle(returnType.isSingle());
} else {
return TypeManager.NULL_REPRESENTATION;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
* It is possible to specify the error message that should be shown if the restrictions aren't followed.
*
* This class shouldn't be used for expressions that should only work with specific {@link TriggerContext}s.
* For this purpose, use {@link ParseContext#getParserState()} in conjuction with {@link ParserState#getCurrentContexts()}.
* For this purpose, use {@link ParseContext#getParserState()} in conjunction with {@link ParserState#getCurrentContexts()}.
* @param <T> the return type
* @see ParserState#getCurrentContexts()
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,7 @@ public static Optional<? extends Expression<Boolean>> parseBooleanExpression(Str
}

recentContextValues.acknowledge(info);
return Optional.of(new ContextExpression<>((ContextValue<?, T>) info, value, alone));
return Optional.of(new ContextExpression<>((ContextValue<TriggerContext, T>) info, value, alone));
}
}
return Optional.empty();
Expand Down
Loading