From 86afaf6e8e675b7478d36c4bdf599516aea73d02 Mon Sep 17 00:00:00 2001 From: Mwexim Date: Wed, 29 Dec 2021 13:32:22 +0100 Subject: [PATCH 1/5] =?UTF-8?q?=E2=9C=A8=20Expression=20(default=20changer?= =?UTF-8?q?s)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../syst3ms/skriptparser/lang/Expression.java | 23 ++++++++++++++----- .../syst3ms/skriptparser/lang/Variable.java | 4 ++-- .../skriptparser/sections/SecConditional.java | 1 - .../skriptparser/types/changers/Changer.java | 4 ++-- 4 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/main/java/io/github/syst3ms/skriptparser/lang/Expression.java b/src/main/java/io/github/syst3ms/skriptparser/lang/Expression.java index 9c343b52..e6d73666 100644 --- a/src/main/java/io/github/syst3ms/skriptparser/lang/Expression.java +++ b/src/main/java/io/github/syst3ms/skriptparser/lang/Expression.java @@ -7,7 +7,9 @@ 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; @@ -74,7 +76,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 getReturnType() { return SyntaxManager.getExpressionExact(this) @@ -93,7 +95,10 @@ default Class getReturnType() { * {@link ChangeMode#DELETE} or {@link ChangeMode#RESET}, then an empty array should be returned. */ default Optional[]> 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)); } /** @@ -128,12 +133,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) ch).change(getArray(ctx), mode, changeWith)); + } /** * @param ctx the event @@ -152,7 +163,7 @@ default Stream 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 the type to convert this Expression to diff --git a/src/main/java/io/github/syst3ms/skriptparser/lang/Variable.java b/src/main/java/io/github/syst3ms/skriptparser/lang/Variable.java index 93e16a38..740eeb79 100644 --- a/src/main/java/io/github/syst3ms/skriptparser/lang/Variable.java +++ b/src/main/java/io/github/syst3ms/skriptparser/lang/Variable.java @@ -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) ch).change(one, new Object[0], ChangeMode.RESET)); + changer.ifPresent(ch -> ((Changer) ch).change(one, ChangeMode.RESET, new Object[0])); } } break; @@ -291,7 +291,7 @@ public void change(TriggerContext ctx, ChangeMode mode, Object[] changeWith) thr if (d2 != null) l.add(d2); } - ((Changer) changer.get()).change(one, l.toArray(), mode); + ((Changer) changer.get()).change(one, mode, l.toArray()); } break; } diff --git a/src/main/java/io/github/syst3ms/skriptparser/sections/SecConditional.java b/src/main/java/io/github/syst3ms/skriptparser/sections/SecConditional.java index db44052a..675d696d 100644 --- a/src/main/java/io/github/syst3ms/skriptparser/sections/SecConditional.java +++ b/src/main/java/io/github/syst3ms/skriptparser/sections/SecConditional.java @@ -122,7 +122,6 @@ public boolean checkFinishing(Predicate finishingTest, */ public void setFallingClause(SecConditional conditional) { fallingClause = conditional; - //fallingClause.setParent(this); } /** diff --git a/src/main/java/io/github/syst3ms/skriptparser/types/changers/Changer.java b/src/main/java/io/github/syst3ms/skriptparser/types/changers/Changer.java index 966e1226..f134305a 100644 --- a/src/main/java/io/github/syst3ms/skriptparser/types/changers/Changer.java +++ b/src/main/java/io/github/syst3ms/skriptparser/types/changers/Changer.java @@ -19,8 +19,8 @@ public interface Changer { /** * Changes the implementing object * @param toChange the current values - * @param changeWith the values to change with * @param mode the change mode + * @param changeWith the values to change with */ - void change(T[] toChange, Object[] changeWith, ChangeMode mode); + void change(T[] toChange, ChangeMode mode, Object[] changeWith); } From 712c7f8206575cd898b8f9f40176a48c0699a9b8 Mon Sep 17 00:00:00 2001 From: Mwexim Date: Wed, 29 Dec 2021 14:58:01 +0100 Subject: [PATCH 2/5] =?UTF-8?q?=F0=9F=93=9D=20RestrictedExpression=20(typo?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../syst3ms/skriptparser/lang/base/RestrictedExpression.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/github/syst3ms/skriptparser/lang/base/RestrictedExpression.java b/src/main/java/io/github/syst3ms/skriptparser/lang/base/RestrictedExpression.java index 9d6bb562..3b1bca33 100644 --- a/src/main/java/io/github/syst3ms/skriptparser/lang/base/RestrictedExpression.java +++ b/src/main/java/io/github/syst3ms/skriptparser/lang/base/RestrictedExpression.java @@ -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 the return type * @see ParserState#getCurrentContexts() */ From 84aa5d98cdc87da0b4d197406504a0d1d208d22d Mon Sep 17 00:00:00 2001 From: Mwexim Date: Sat, 1 Jan 2022 15:35:42 +0100 Subject: [PATCH 3/5] =?UTF-8?q?=E2=9C=A8=20ExpressionElement=20&=20EventEx?= =?UTF-8?q?pression=20(support=20for=20default=20values)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../skriptparser/effects/EffChange.java | 2 +- .../skriptparser/lang/SimpleExpression.java | 20 +---- .../skriptparser/lang/SimpleLiteral.java | 15 ++-- .../lang/base/EventExpression.java | 80 +++++++++++++++++++ .../skriptparser/parsing/SyntaxParser.java | 2 +- .../pattern/ExpressionElement.java | 20 ++++- .../skriptparser/pattern/PatternParser.java | 17 ++-- .../registration/DefaultRegistration.java | 6 +- .../registration/SkriptRegistration.java | 16 +++- .../syst3ms/skriptparser/types/Type.java | 31 +++++-- .../skriptparser/TestRegistration.java | 9 +++ .../parsing/PatternParserTest.java | 12 ++- .../parsing/SyntaxParserTest.java | 21 ++++- .../syst3ms/skriptparser/syntax/EvtTest.java | 5 +- 14 files changed, 207 insertions(+), 49 deletions(-) create mode 100644 src/main/java/io/github/syst3ms/skriptparser/lang/base/EventExpression.java diff --git a/src/main/java/io/github/syst3ms/skriptparser/effects/EffChange.java b/src/main/java/io/github/syst3ms/skriptparser/effects/EffChange.java index b58353db..c62cca17 100644 --- a/src/main/java/io/github/syst3ms/skriptparser/effects/EffChange.java +++ b/src/main/java/io/github/syst3ms/skriptparser/effects/EffChange.java @@ -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); diff --git a/src/main/java/io/github/syst3ms/skriptparser/lang/SimpleExpression.java b/src/main/java/io/github/syst3ms/skriptparser/lang/SimpleExpression.java index da236469..940ae5c6 100644 --- a/src/main/java/io/github/syst3ms/skriptparser/lang/SimpleExpression.java +++ b/src/main/java/io/github/syst3ms/skriptparser/lang/SimpleExpression.java @@ -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; @@ -15,18 +13,13 @@ public class SimpleExpression implements Expression { private final Class returnType; private final boolean isSingle; private final Function function; - @Nullable - private final String representation; + private final String toString; - public SimpleExpression(Class returnType, boolean isSingle, Function function) { - this(returnType, isSingle, function, null); - } - - public SimpleExpression(Class returnType, boolean isSingle, Function function, @Nullable String representation) { + public SimpleExpression(Class returnType, boolean isSingle, Function function, String toString) { this.returnType = returnType; this.isSingle = isSingle; this.function = function; - this.representation = representation; + this.toString = toString; } @Override @@ -51,11 +44,6 @@ public Class 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; } } diff --git a/src/main/java/io/github/syst3ms/skriptparser/lang/SimpleLiteral.java b/src/main/java/io/github/syst3ms/skriptparser/lang/SimpleLiteral.java index 4f7ebc0e..fbf3e97e 100644 --- a/src/main/java/io/github/syst3ms/skriptparser/lang/SimpleLiteral.java +++ b/src/main/java/io/github/syst3ms/skriptparser/lang/SimpleLiteral.java @@ -21,14 +21,15 @@ public class SimpleLiteral implements Literal { private final T[] values; private boolean isAndList = true; - private Class returnType; + private final Class returnType; - public SimpleLiteral(T[] values) { - this.values = values; + public SimpleLiteral(T... values) { + this((Class) values.getClass().getComponentType(), values); } @SafeVarargs - public SimpleLiteral(Class c, T... values) { + public SimpleLiteral(Class returnType, T... values) { + this.returnType = returnType; this.values = Arrays.copyOf(values, values.length); } @@ -60,11 +61,7 @@ public boolean isSingle() { } public Class getReturnType() { - if (returnType != null) { - return returnType; - } else { - return returnType = (Class) values.getClass().getComponentType(); - } + return returnType; } @Override diff --git a/src/main/java/io/github/syst3ms/skriptparser/lang/base/EventExpression.java b/src/main/java/io/github/syst3ms/skriptparser/lang/base/EventExpression.java new file mode 100644 index 00000000..44ef8130 --- /dev/null +++ b/src/main/java/io/github/syst3ms/skriptparser/lang/base/EventExpression.java @@ -0,0 +1,80 @@ +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.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.Type; +import org.jetbrains.annotations.Contract; + +import java.util.Optional; +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. + *
+ * Note that the {@linkplain ContextValue.State state} of the referenced context value must be 'present'. + * @param the Expression's type + * @author Mwexim + */ +public class EventExpression implements Expression { + private final Class returnType; + private final boolean isSingle; + private ContextValue info; + + public EventExpression(Class returnType, boolean isSingle) { + this.returnType = returnType; + this.isSingle = isSingle; + } + + @Override + @Contract("_, _, _ -> fail") + public boolean init(Expression[] expressions, int matchedPattern, ParseContext parseContext) { + throw new UnsupportedOperationException(); + } + + @Override + public T[] getValues(TriggerContext ctx) { + if (info == null) + info = getContextValue(ctx, returnType, isSingle); + return info.getFunction().apply(ctx); + } + + @Override + public boolean isSingle() { + return isSingle; + } + + @Override + public Class getReturnType() { + return returnType; + } + + @Override + public String toString(TriggerContext ctx, boolean debug) { + if (info == null) + info = getContextValue(ctx, returnType, isSingle); + return new String[] {"past ", "", "future "}[info.getState().ordinal()] + + (info.getUsage().isCorrect(true) ? "" : "context-") + + info.getReturnType().getType().withIndefiniteArticle(isSingle); + } + + @SuppressWarnings("unchecked") + private static ContextValue getContextValue(TriggerContext ctx, Class returnType, boolean isSingle) { + var possibilities = ContextValues.getContextValues(ctx.getClass()).stream() + .filter(info -> returnType.isAssignableFrom(info.getReturnType().getType().getTypeClass())) + .filter(info -> !isSingle || info.getReturnType().isSingle()) + .filter(info -> info.getState() == ContextValue.State.PRESENT) + .collect(Collectors.toList()); + if (possibilities.size() > 1) + throw new SkriptRuntimeException("Expected exactly one match, but found multiple " + (isSingle ? "" : "non-") + "single context values corresponding to the class '" + returnType.getName() + "'"); + return Optional.ofNullable(possibilities.get(0)) + .map(info -> (ContextValue) info) + .orElseThrow(() -> new SkriptRuntimeException("Couldn't find a" + (isSingle ? "" : "non-") + "single context value corresponding to the class '" + returnType.getName() + "'")); + } +} \ No newline at end of file diff --git a/src/main/java/io/github/syst3ms/skriptparser/parsing/SyntaxParser.java b/src/main/java/io/github/syst3ms/skriptparser/parsing/SyntaxParser.java index 86a5a2c6..59cf7dbf 100644 --- a/src/main/java/io/github/syst3ms/skriptparser/parsing/SyntaxParser.java +++ b/src/main/java/io/github/syst3ms/skriptparser/parsing/SyntaxParser.java @@ -335,7 +335,7 @@ public static Optional> parseBooleanExpression(Str } recentContextValues.acknowledge(info); - return Optional.of(new ContextExpression<>((ContextValue) info, value, alone)); + return Optional.of(new ContextExpression<>((ContextValue) info, value, alone)); } } return Optional.empty(); diff --git a/src/main/java/io/github/syst3ms/skriptparser/pattern/ExpressionElement.java b/src/main/java/io/github/syst3ms/skriptparser/pattern/ExpressionElement.java index e0fc1f34..bcb8b749 100644 --- a/src/main/java/io/github/syst3ms/skriptparser/pattern/ExpressionElement.java +++ b/src/main/java/io/github/syst3ms/skriptparser/pattern/ExpressionElement.java @@ -46,6 +46,8 @@ public ExpressionElement(List> types, Acceptance acceptance, bool public int match(String s, int index, MatchContext context) { var typeArray = types.toArray(new PatternType[0]); if (index >= s.length()) { + if (typeArray.length >= 1) + getDefaultExpression(typeArray[0]).ifPresent(context::addExpression); return -1; } var logger = context.getLogger(); @@ -217,14 +219,30 @@ private Optional> parse(String s, PatternT return false; } break; + default: + throw new IllegalStateException(); } return true; }); + if (expression.isEmpty()) { + expression = getDefaultExpression(type); + } return expression; } return Optional.empty(); } + @SuppressWarnings("unchecked") + private Optional> getDefaultExpression(PatternType type) { + if (nullable) { + return type.getType().getDefaultExpression() + .filter(expr -> !type.isSingle() || type.isSingle() && expr.isSingle()) + .map(expr -> (Expression) expr); + } else { + return Optional.empty(); + } + } + @Override public boolean equals(Object obj) { if (this == obj) @@ -233,7 +251,7 @@ public boolean equals(Object obj) { return false; } else { var e = (ExpressionElement) obj; - return types.equals(e.types) && acceptance == e.acceptance && acceptsConditional == e.acceptsConditional; + return types.equals(e.types) && acceptance == e.acceptance && nullable == e.nullable && acceptsConditional == e.acceptsConditional; } } diff --git a/src/main/java/io/github/syst3ms/skriptparser/pattern/PatternParser.java b/src/main/java/io/github/syst3ms/skriptparser/pattern/PatternParser.java index 9ab4b082..b0cba087 100644 --- a/src/main/java/io/github/syst3ms/skriptparser/pattern/PatternParser.java +++ b/src/main/java/io/github/syst3ms/skriptparser/pattern/PatternParser.java @@ -18,7 +18,7 @@ */ public class PatternParser { private static final Pattern PARSE_MARK_PATTERN = Pattern.compile("(([A-Za-z0-9]+)?):(.*)"); - private static final Pattern VARIABLE_PATTERN = Pattern.compile("(-)?([*^~])?(=)?(?[\\w/]+)?"); + private static final Pattern VARIABLE_PATTERN = Pattern.compile("([*^~])?(=)?(?[\\w/]+)?(\\?)?"); /** * Parses a pattern and returns a {@link PatternElement}. This method can be called by itself, for example when parsing group constructs. @@ -194,14 +194,14 @@ private static Optional parsePattern(String pattern, S var s = pattern.substring(i + 1, nextIndex); i = nextIndex; var m = VARIABLE_PATTERN.matcher(s); + if (!m.matches()) { logger.error("Invalid expression element (index " + initialPos + ") : '" + s + "'", ErrorType.MALFORMED_INPUT); return Optional.empty(); } else { - var nullable = m.group(1) != null; var acceptance = ExpressionElement.Acceptance.ALL; - if (m.group(2) != null) { - var acc = m.group(2); + if (m.group(1) != null) { + var acc = m.group(1); if (acc.equals("~")) { acceptance = ExpressionElement.Acceptance.EXPRESSIONS_ONLY; } else if (acc.equals("^")) { @@ -221,10 +221,17 @@ private static Optional parsePattern(String pattern, S } patternTypes.add(t.get()); } - var acceptConditional = m.group(3) != null; + var acceptConditional = m.group(2) != null; + var nullable = m.group(4) != null; if (acceptConditional && patternTypes.stream().noneMatch(t -> t.getType().getTypeClass() == Boolean.class)) { logger.error("Can't use the '=' flag on non-boolean types (index " + initialPos + ")", ErrorType.SEMANTIC_ERROR); return Optional.empty(); + } else if (nullable && patternTypes.size() > 1) { + logger.error("Can't use the '?' flag with multiple type possibilities (index " + initialPos + ")", ErrorType.SEMANTIC_ERROR); + return Optional.empty(); + } else if (nullable && patternTypes.get(0).getType().getDefaultExpression().isEmpty()) { + logger.error("Can't use the '?' flag if the type doesn't have a default expression (index " + initialPos + ")", ErrorType.SEMANTIC_ERROR); + return Optional.empty(); } elements.add(new ExpressionElement(patternTypes, acceptance, nullable, acceptConditional)); } diff --git a/src/main/java/io/github/syst3ms/skriptparser/registration/DefaultRegistration.java b/src/main/java/io/github/syst3ms/skriptparser/registration/DefaultRegistration.java index cd434eeb..92b354ac 100644 --- a/src/main/java/io/github/syst3ms/skriptparser/registration/DefaultRegistration.java +++ b/src/main/java/io/github/syst3ms/skriptparser/registration/DefaultRegistration.java @@ -1,6 +1,7 @@ package io.github.syst3ms.skriptparser.registration; import io.github.syst3ms.skriptparser.Parser; +import io.github.syst3ms.skriptparser.lang.SimpleLiteral; import io.github.syst3ms.skriptparser.types.Type; import io.github.syst3ms.skriptparser.types.TypeManager; import io.github.syst3ms.skriptparser.types.changers.Arithmetic; @@ -157,7 +158,6 @@ public Class getRelativeType() { return null; } }) - .toStringFunction(String::valueOf) .register(); registration.newType(Type.class, "type", "type@s") @@ -167,10 +167,10 @@ public Class getRelativeType() { registration.newType(Color.class, "color", "color@s") .literalParser(s -> Color.ofLiteral(s).orElse(null)) - .toStringFunction(Color::toString) .register(); registration.newType(Duration.class, "duration", "duration@s") + .defaultExpression(new SimpleLiteral<>(Duration.ZERO)) .literalParser(s -> TimeUtils.parseDuration(s).orElse(null)) .toStringFunction(TimeUtils::toStringDuration) .arithmetic(new Arithmetic() { @@ -197,7 +197,6 @@ public Class getRelativeType() { .register(); registration.newType(SkriptDate.class, "date", "date@s") - .toStringFunction(SkriptDate::toString) .arithmetic(new Arithmetic() { @Override public Duration difference(SkriptDate first, SkriptDate second) { @@ -223,7 +222,6 @@ public Class getRelativeType() { registration.newType(Time.class, "time", "time@s") .literalParser(s -> Time.parse(s).orElse(null)) - .toStringFunction(Time::toString) .arithmetic(new Arithmetic() { @Override public Duration difference(Time first, Time second) { diff --git a/src/main/java/io/github/syst3ms/skriptparser/registration/SkriptRegistration.java b/src/main/java/io/github/syst3ms/skriptparser/registration/SkriptRegistration.java index 81b11b12..35b294f0 100644 --- a/src/main/java/io/github/syst3ms/skriptparser/registration/SkriptRegistration.java +++ b/src/main/java/io/github/syst3ms/skriptparser/registration/SkriptRegistration.java @@ -558,6 +558,8 @@ public class TypeRegistrar implements Registrar { private final Class c; private final String baseName; private final String pattern; + @Nullable + private Expression defaultExpression; private Function toStringFunction = o -> Objects.toString(o, TypeManager.NULL_REPRESENTATION); @Nullable private Function literalParser; @@ -572,6 +574,18 @@ public TypeRegistrar(Class c, String baseName, String pattern) { this.pattern = pattern; } + /** + * The default expression will be used when an optional part in a pattern, + * with an {@linkplain ExpressionElement}, explicitly allows the expression + * to be set to a default one if the optional part was not used or matched. + * @param defaultExpression the default expression of this type + * @return the registrar + */ + public TypeRegistrar defaultExpression(Expression defaultExpression) { + this.defaultExpression = defaultExpression; + return this; + } + /** * @param literalParser a function interpreting a string as an instance of the type * @return the registrar @@ -614,7 +628,7 @@ public TypeRegistrar arithmetic(Arithmetic arithmetic) { @Override public void register() { newTypes = true; - types.add(new Type<>(c, baseName, pattern, literalParser, toStringFunction, defaultChanger, arithmetic)); + types.add(new Type<>(c, baseName, pattern, literalParser, toStringFunction, defaultChanger, arithmetic, defaultExpression)); } } diff --git a/src/main/java/io/github/syst3ms/skriptparser/types/Type.java b/src/main/java/io/github/syst3ms/skriptparser/types/Type.java index 5cfc651f..57929a64 100644 --- a/src/main/java/io/github/syst3ms/skriptparser/types/Type.java +++ b/src/main/java/io/github/syst3ms/skriptparser/types/Type.java @@ -1,5 +1,7 @@ package io.github.syst3ms.skriptparser.types; +import io.github.syst3ms.skriptparser.lang.Expression; +import io.github.syst3ms.skriptparser.pattern.ExpressionElement; import io.github.syst3ms.skriptparser.types.changers.Arithmetic; import io.github.syst3ms.skriptparser.types.changers.Changer; import io.github.syst3ms.skriptparser.util.StringUtils; @@ -18,6 +20,8 @@ public class Type { private final Class typeClass; private final String baseName; private final String[] pluralForms; + @Nullable + private final Expression defaultExpression; private final Function toStringFunction; @Nullable private final Function literalParser; @@ -84,7 +88,7 @@ public Type(Class typeClass, String pattern, @Nullable Function literalParser, Function toStringFunction) { - this(typeClass, baseName, pattern, literalParser, toStringFunction, null); + this(typeClass, baseName, pattern, literalParser, toStringFunction, null, null); } public Type(Class typeClass, @@ -92,8 +96,9 @@ public Type(Class typeClass, String pattern, @Nullable Function literalParser, Function toStringFunction, - @Nullable Changer defaultChanger) { - this(typeClass, baseName, pattern, literalParser, toStringFunction, defaultChanger, null); + @Nullable Changer defaultChanger, + @Nullable Arithmetic arithmetic) { + this(typeClass, baseName, pattern, literalParser, toStringFunction, defaultChanger, arithmetic, null); } @SuppressWarnings("unchecked") @@ -103,7 +108,8 @@ public Type(Class typeClass, @Nullable Function literalParser, Function toStringFunction, @Nullable Changer defaultChanger, - @Nullable Arithmetic arithmetic) { + @Nullable Arithmetic arithmetic, + @Nullable Expression defaultExpression) { this.typeClass = typeClass; this.baseName = baseName; this.literalParser = literalParser; @@ -111,6 +117,7 @@ public Type(Class typeClass, this.pluralForms = StringUtils.getForms(pattern.strip()); this.defaultChanger = defaultChanger; this.arithmetic = arithmetic; + this.defaultExpression = defaultExpression; } public Class getTypeClass() { @@ -141,13 +148,23 @@ public Optional> getDefaultChanger() { return Optional.ofNullable(arithmetic); } + /** + * The default expression will be used when an optional part in a pattern, + * with an {@linkplain ExpressionElement}, explicitly allows the expression + * to be set to a default one if the optional part was not used or matched. + * @return the default expression + */ + public Optional> getDefaultExpression() { + return Optional.ofNullable(defaultExpression); + } + /** * Adds a proper English indefinite article to this type and applies the correct form. - * @param plural whether this Type is plural or not + * @param isSingle whether this Type is single or not * @return the applied form of this Type */ - public String withIndefiniteArticle(boolean plural) { - return StringUtils.withIndefiniteArticle(pluralForms[plural ? 1 : 0], plural); + public String withIndefiniteArticle(boolean isSingle) { + return StringUtils.withIndefiniteArticle(pluralForms[isSingle ? 0 : 1], isSingle); } @Override diff --git a/src/test/java/io/github/syst3ms/skriptparser/TestRegistration.java b/src/test/java/io/github/syst3ms/skriptparser/TestRegistration.java index a3e6f8cb..f2bfc3a1 100644 --- a/src/test/java/io/github/syst3ms/skriptparser/TestRegistration.java +++ b/src/test/java/io/github/syst3ms/skriptparser/TestRegistration.java @@ -1,5 +1,6 @@ package io.github.syst3ms.skriptparser; +import io.github.syst3ms.skriptparser.lang.base.EventExpression; import io.github.syst3ms.skriptparser.registration.DefaultRegistration; import io.github.syst3ms.skriptparser.registration.SkriptRegistration; import io.github.syst3ms.skriptparser.util.FileUtils; @@ -21,7 +22,9 @@ public static void register() { } catch (NoSuchFieldException | IllegalAccessException e) { e.printStackTrace(); } + DefaultRegistration.register(); + try { FileUtils.loadClasses( Path.of("build/classes/java/main"), @@ -40,6 +43,12 @@ public static void register() { } catch (IOException e) { e.printStackTrace(); } + + // This overrides the default String type + Parser.getMainRegistration().newType(String.class, "string", "string@s") + .defaultExpression(new EventExpression<>(String.class, true)) + .register(); + Parser.getMainRegistration().register(); /* diff --git a/src/test/java/io/github/syst3ms/skriptparser/parsing/PatternParserTest.java b/src/test/java/io/github/syst3ms/skriptparser/parsing/PatternParserTest.java index a80c4773..9d405930 100644 --- a/src/test/java/io/github/syst3ms/skriptparser/parsing/PatternParserTest.java +++ b/src/test/java/io/github/syst3ms/skriptparser/parsing/PatternParserTest.java @@ -168,11 +168,21 @@ public void testParsePattern() { TypeManager.getPatternType("strings").orElseThrow(AssertionError::new) ), ExpressionElement.Acceptance.LITERALS_ONLY, - true, + false, false ), parsePattern("%*number/strings%", logger) ); + assertEqualsOptional( + new ExpressionElement( + Collections.singletonList(TypeManager.getPatternType("durations").orElseThrow(AssertionError::new)), + ExpressionElement.Acceptance.VARIABLES_ONLY, + true, + false + ), + parsePattern("%^durations?%", logger) + ); + assertOptionalEmpty(parsePattern("%number/duration/date?%", logger)); // Multiple types cannot be nullable // Failing patterns assertOptionalEmpty(parsePattern("(unclosed", logger)); diff --git a/src/test/java/io/github/syst3ms/skriptparser/parsing/SyntaxParserTest.java b/src/test/java/io/github/syst3ms/skriptparser/parsing/SyntaxParserTest.java index 005ea955..9e9ace2e 100644 --- a/src/test/java/io/github/syst3ms/skriptparser/parsing/SyntaxParserTest.java +++ b/src/test/java/io/github/syst3ms/skriptparser/parsing/SyntaxParserTest.java @@ -1,10 +1,16 @@ package io.github.syst3ms.skriptparser.parsing; import io.github.syst3ms.skriptparser.TestRegistration; +import io.github.syst3ms.skriptparser.lang.base.EventExpression; import io.github.syst3ms.skriptparser.log.LogEntry; import io.github.syst3ms.skriptparser.log.LogType; +import io.github.syst3ms.skriptparser.log.SkriptLogger; +import io.github.syst3ms.skriptparser.pattern.PatternParser; import io.github.syst3ms.skriptparser.registration.SkriptAddon; +import io.github.syst3ms.skriptparser.syntax.TestContext; +import io.github.syst3ms.skriptparser.types.TypeManager; import io.github.syst3ms.skriptparser.variables.Variables; +import org.junit.Test; import org.junit.jupiter.api.DynamicContainer; import org.junit.jupiter.api.DynamicNode; import org.junit.jupiter.api.DynamicTest; @@ -36,8 +42,21 @@ public class SyntaxParserTest { private static final List errorsFound = new ArrayList<>(); + @Test + public void testDefaultExpression() { + assert TypeManager.getByClassExact(String.class).orElseThrow().getDefaultExpression().isPresent(); + + var pattern = PatternParser.parsePattern("look for default expression [%strings?%]", new SkriptLogger()).orElseThrow(AssertionError::new); + var context = new MatchContext(pattern, new ParserState(), new SkriptLogger()); + assert pattern.match("look for default expression", 0, context) != -1 : "pattern didn't match"; + + var expr = context.getParsedExpressions().size() >= 1 ? context.getParsedExpressions().get(0) : null; + assert expr instanceof EventExpression : "didn't find default expression of type EventExpression"; + assert expr.getValues(new TestContext())[0].equals("This works as well"); + } + @TestFactory - public Iterator syntaxTest() { + public Iterator testSyntaxClasses() { String[] folders = {"effects", "expressions", "literals", "sections", "tags", "general"}; ArrayList containerList = new ArrayList<>(); for (String folder : folders) { diff --git a/src/test/java/io/github/syst3ms/skriptparser/syntax/EvtTest.java b/src/test/java/io/github/syst3ms/skriptparser/syntax/EvtTest.java index 4480f16a..3a17f3ae 100644 --- a/src/test/java/io/github/syst3ms/skriptparser/syntax/EvtTest.java +++ b/src/test/java/io/github/syst3ms/skriptparser/syntax/EvtTest.java @@ -5,7 +5,7 @@ import io.github.syst3ms.skriptparser.lang.SkriptEvent; import io.github.syst3ms.skriptparser.lang.TriggerContext; import io.github.syst3ms.skriptparser.parsing.ParseContext; -import io.github.syst3ms.skriptparser.registration.context.ContextValue; +import io.github.syst3ms.skriptparser.registration.context.ContextValue.State; import io.github.syst3ms.skriptparser.registration.context.ContextValue.Usage; import io.github.syst3ms.skriptparser.syntax.TestContext.SubTestContext; @@ -38,9 +38,10 @@ public class EvtTest extends SkriptEvent { Parser.getMainRegistration() .newContextValue(TestContext.class, String.class, true, "[some] pattern value", ctx -> new String[] {ctx.patternValue()}) .setUsage(Usage.ALONE_ONLY) - .setState(ContextValue.State.PAST) + .setState(State.PAST) .register(); Parser.getMainRegistration().addContextType(SubTestContext.class, Duration.class, SubTestContext::oneDay); + Parser.getMainRegistration().addContextType(TestContext.class, String.class, __ -> "This works as well"); } private Expression condition; From 9fc5da627082d781ced5ab71a17d442e51bfbc52 Mon Sep 17 00:00:00 2001 From: Mwexim Date: Tue, 4 Jan 2022 16:09:01 +0100 Subject: [PATCH 4/5] =?UTF-8?q?=F0=9F=93=9D=20SyntaxParserTest=20(improved?= =?UTF-8?q?=20tests)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../parsing/SyntaxParserTest.java | 34 ++++++++++++++----- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/src/test/java/io/github/syst3ms/skriptparser/parsing/SyntaxParserTest.java b/src/test/java/io/github/syst3ms/skriptparser/parsing/SyntaxParserTest.java index 9e9ace2e..0b605a3f 100644 --- a/src/test/java/io/github/syst3ms/skriptparser/parsing/SyntaxParserTest.java +++ b/src/test/java/io/github/syst3ms/skriptparser/parsing/SyntaxParserTest.java @@ -1,6 +1,8 @@ package io.github.syst3ms.skriptparser.parsing; import io.github.syst3ms.skriptparser.TestRegistration; +import io.github.syst3ms.skriptparser.lang.SimpleLiteral; +import io.github.syst3ms.skriptparser.lang.TriggerContext; import io.github.syst3ms.skriptparser.lang.base.EventExpression; import io.github.syst3ms.skriptparser.log.LogEntry; import io.github.syst3ms.skriptparser.log.LogType; @@ -8,7 +10,6 @@ import io.github.syst3ms.skriptparser.pattern.PatternParser; import io.github.syst3ms.skriptparser.registration.SkriptAddon; import io.github.syst3ms.skriptparser.syntax.TestContext; -import io.github.syst3ms.skriptparser.types.TypeManager; import io.github.syst3ms.skriptparser.variables.Variables; import org.junit.Test; import org.junit.jupiter.api.DynamicContainer; @@ -19,6 +20,7 @@ import java.io.File; import java.net.URL; +import java.time.Duration; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; @@ -43,16 +45,32 @@ public class SyntaxParserTest { private static final List errorsFound = new ArrayList<>(); @Test - public void testDefaultExpression() { - assert TypeManager.getByClassExact(String.class).orElseThrow().getDefaultExpression().isPresent(); - - var pattern = PatternParser.parsePattern("look for default expression [%strings?%]", new SkriptLogger()).orElseThrow(AssertionError::new); + public void testDefaultExpressions() { + /* + * Duration type with SimpleLiteral as default expression + * Also checks if non-single types accept single default expressions. + */ + var pattern = PatternParser.parsePattern("look for default expression [%durations?%]", new SkriptLogger()).orElseThrow(AssertionError::new); var context = new MatchContext(pattern, new ParserState(), new SkriptLogger()); assert pattern.match("look for default expression", 0, context) != -1 : "pattern didn't match"; - var expr = context.getParsedExpressions().size() >= 1 ? context.getParsedExpressions().get(0) : null; - assert expr instanceof EventExpression : "didn't find default expression of type EventExpression"; - assert expr.getValues(new TestContext())[0].equals("This works as well"); + var expressions = context.getParsedExpressions(); + assert expressions.size() == 1 : "expected exactly one (default) expression, found " + expressions.size(); + assert expressions.get(0) instanceof SimpleLiteral : "didn't find default expression of type SimpleLiteral"; + assert expressions.get(0).getSingle(TriggerContext.DUMMY).orElseThrow(AssertionError::new).equals(Duration.ZERO) : "default expression is not equal to Duration#ZERO constant"; + + /* + * String type with EventExpression as default expression + */ + pattern = PatternParser.parsePattern("another default expression [%string?%]", new SkriptLogger()).orElseThrow(AssertionError::new); + context = new MatchContext(pattern, new ParserState(), new SkriptLogger()); + assert pattern.match("another default expression", 0, context) != -1 : "pattern didn't match"; + + expressions = context.getParsedExpressions(); + assert expressions.size() == 1 : "expected exactly one (default) expression, found " + expressions.size(); + assert expressions.get(0) instanceof EventExpression : "didn't find default expression of type EventExpression"; + // We use TestContext to eliminate all other context values that purposefully have been declared with SubTestContext + assert expressions.get(0).getSingle(new TestContext()).orElseThrow(AssertionError::new).equals("This works as well") : "default expression is not equal to 'This works as well'"; } @TestFactory From b4eaf03605ee751cd6fc62c6c236adab982391ee Mon Sep 17 00:00:00 2001 From: Mwexim Date: Wed, 5 Jan 2022 15:26:55 +0100 Subject: [PATCH 5/5] =?UTF-8?q?=E2=9C=A8=20EventExpression=20(made=20it=20?= =?UTF-8?q?log=20the=20errors=20instead=20of=20throwing)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../syst3ms/skriptparser/lang/Expression.java | 9 +++ .../lang/base/EventExpression.java | 74 +++++++++++-------- .../pattern/ExpressionElement.java | 30 ++++---- .../registration/context/ContextValues.java | 6 +- .../parsing/SyntaxParserTest.java | 6 +- .../syst3ms/skriptparser/syntax/EvtTest.java | 2 +- 6 files changed, 76 insertions(+), 51 deletions(-) diff --git a/src/main/java/io/github/syst3ms/skriptparser/lang/Expression.java b/src/main/java/io/github/syst3ms/skriptparser/lang/Expression.java index e6d73666..5d530b18 100644 --- a/src/main/java/io/github/syst3ms/skriptparser/lang/Expression.java +++ b/src/main/java/io/github/syst3ms/skriptparser/lang/Expression.java @@ -2,6 +2,7 @@ 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; @@ -243,6 +244,14 @@ static boolean check(T[] all, Predicate 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 List getMatchingSections(ParserState parserState, Class sectionClass) { diff --git a/src/main/java/io/github/syst3ms/skriptparser/lang/base/EventExpression.java b/src/main/java/io/github/syst3ms/skriptparser/lang/base/EventExpression.java index 44ef8130..8b40e2d0 100644 --- a/src/main/java/io/github/syst3ms/skriptparser/lang/base/EventExpression.java +++ b/src/main/java/io/github/syst3ms/skriptparser/lang/base/EventExpression.java @@ -2,14 +2,17 @@ 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 org.jetbrains.annotations.Contract; +import io.github.syst3ms.skriptparser.types.TypeManager; -import java.util.Optional; +import java.util.ArrayList; +import java.util.List; import java.util.stream.Collectors; /** @@ -23,58 +26,65 @@ * @author Mwexim */ public class EventExpression implements Expression { - private final Class returnType; - private final boolean isSingle; - private ContextValue info; + private final PatternType returnType; + private final List> contextValues = new ArrayList<>(); public EventExpression(Class returnType, boolean isSingle) { - this.returnType = returnType; - this.isSingle = isSingle; + this.returnType = new PatternType<>(TypeManager.getByClassExact(returnType).orElseThrow(IllegalArgumentException::new), isSingle); } @Override - @Contract("_, _, _ -> fail") public boolean init(Expression[] expressions, int matchedPattern, ParseContext parseContext) { - throw new UnsupportedOperationException(); + 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) { - if (info == null) - info = getContextValue(ctx, returnType, isSingle); + var info = (ContextValue) 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 isSingle; + return returnType.isSingle(); } @Override public Class getReturnType() { - return returnType; + return returnType.getType().getTypeClass(); } + @SuppressWarnings("unchecked") @Override public String toString(TriggerContext ctx, boolean debug) { - if (info == null) - info = getContextValue(ctx, returnType, isSingle); - return new String[] {"past ", "", "future "}[info.getState().ordinal()] - + (info.getUsage().isCorrect(true) ? "" : "context-") - + info.getReturnType().getType().withIndefiniteArticle(isSingle); - } - - @SuppressWarnings("unchecked") - private static ContextValue getContextValue(TriggerContext ctx, Class returnType, boolean isSingle) { - var possibilities = ContextValues.getContextValues(ctx.getClass()).stream() - .filter(info -> returnType.isAssignableFrom(info.getReturnType().getType().getTypeClass())) - .filter(info -> !isSingle || info.getReturnType().isSingle()) - .filter(info -> info.getState() == ContextValue.State.PRESENT) - .collect(Collectors.toList()); - if (possibilities.size() > 1) - throw new SkriptRuntimeException("Expected exactly one match, but found multiple " + (isSingle ? "" : "non-") + "single context values corresponding to the class '" + returnType.getName() + "'"); - return Optional.ofNullable(possibilities.get(0)) - .map(info -> (ContextValue) info) - .orElseThrow(() -> new SkriptRuntimeException("Couldn't find a" + (isSingle ? "" : "non-") + "single context value corresponding to the class '" + returnType.getName() + "'")); + var info = (ContextValue) 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; + } } } \ No newline at end of file diff --git a/src/main/java/io/github/syst3ms/skriptparser/pattern/ExpressionElement.java b/src/main/java/io/github/syst3ms/skriptparser/pattern/ExpressionElement.java index bcb8b749..96b58b68 100644 --- a/src/main/java/io/github/syst3ms/skriptparser/pattern/ExpressionElement.java +++ b/src/main/java/io/github/syst3ms/skriptparser/pattern/ExpressionElement.java @@ -5,9 +5,7 @@ import io.github.syst3ms.skriptparser.lang.Variable; import io.github.syst3ms.skriptparser.lang.base.ConditionalExpression; import io.github.syst3ms.skriptparser.log.ErrorType; -import io.github.syst3ms.skriptparser.log.SkriptLogger; import io.github.syst3ms.skriptparser.parsing.MatchContext; -import io.github.syst3ms.skriptparser.parsing.ParserState; import io.github.syst3ms.skriptparser.parsing.SyntaxParser; import io.github.syst3ms.skriptparser.types.PatternType; import io.github.syst3ms.skriptparser.util.StringUtils; @@ -47,7 +45,7 @@ public int match(String s, int index, MatchContext context) { var typeArray = types.toArray(new PatternType[0]); if (index >= s.length()) { if (typeArray.length >= 1) - getDefaultExpression(typeArray[0]).ifPresent(context::addExpression); + getDefaultExpression(typeArray[0], context).ifPresent(context::addExpression); return -1; } var logger = context.getLogger(); @@ -71,7 +69,7 @@ public int match(String s, int index, MatchContext context) { return -1; } var toParse = s.substring(index).strip(); - var expression = parse(toParse, typeArray, context.getParserState(), logger); + var expression = parse(toParse, typeArray, context); if (expression.isPresent()) { context.addExpression(expression.get()); return index + toParse.length(); @@ -81,7 +79,7 @@ public int match(String s, int index, MatchContext context) { var i = StringUtils.indexOfIgnoreCase(s, text, index); while (i != -1) { var toParse = s.substring(index, i).strip(); - var expression = parse(toParse, typeArray, context.getParserState(), logger); + var expression = parse(toParse, typeArray, context); if (expression.isPresent()) { context.addExpression(expression.get()); return index + toParse.length(); @@ -98,7 +96,7 @@ public int match(String s, int index, MatchContext context) { var toParse = s.substring(index, i); if (toParse.length() == context.getOriginalPattern().length()) continue; - var expression = parse(toParse, typeArray, context.getParserState(), logger); + var expression = parse(toParse, typeArray, context); if (expression.isPresent()) { context.addExpression(expression.get()); return index + toParse.length(); @@ -119,7 +117,7 @@ public int match(String s, int index, MatchContext context) { var i = StringUtils.indexOfIgnoreCase(s, split, index); if (i != -1) { var toParse = s.substring(index, i); - var expression = parse(toParse, typeArray, context.getParserState(), logger); + var expression = parse(toParse, typeArray, context); if (expression.isPresent()) { context.addExpression(expression.get()); return index + toParse.length(); @@ -138,7 +136,7 @@ public int match(String s, int index, MatchContext context) { var i = StringUtils.indexOfIgnoreCase(s, split, index); if (i != -1) { var toParse = s.substring(index, i); - var expression = parse(toParse, typeArray, context.getParserState(), logger); + var expression = parse(toParse, typeArray, context); if (expression.isPresent()) { context.addExpression(expression.get()); return index + toParse.length(); @@ -179,7 +177,8 @@ private List splitAtSpaces(String s) { } @SuppressWarnings("unchecked") - private Optional> parse(String s, PatternType[] types, ParserState parserState, SkriptLogger logger) { + private Optional> parse(String s, PatternType[] types, MatchContext context) { + var logger = context.getLogger(); for (var type : types) { Optional> expression; logger.recurse(); @@ -188,11 +187,11 @@ private Optional> parse(String s, PatternT expression = (Optional>) SyntaxParser.parseBooleanExpression( s, acceptsConditional ? SyntaxParser.MAYBE_CONDITIONAL : SyntaxParser.NOT_CONDITIONAL, - parserState, + context.getParserState(), logger ); } else { - expression = SyntaxParser.parseExpression(s, (PatternType) type, parserState, logger); + expression = SyntaxParser.parseExpression(s, (PatternType) type, context.getParserState(), logger); } logger.callback(); if (expression.isEmpty()) @@ -225,7 +224,7 @@ private Optional> parse(String s, PatternT return true; }); if (expression.isEmpty()) { - expression = getDefaultExpression(type); + expression = getDefaultExpression(type, context); } return expression; } @@ -233,10 +232,15 @@ private Optional> parse(String s, PatternT } @SuppressWarnings("unchecked") - private Optional> getDefaultExpression(PatternType type) { + private Optional> getDefaultExpression(PatternType type, MatchContext context) { if (nullable) { return type.getType().getDefaultExpression() .filter(expr -> !type.isSingle() || type.isSingle() && expr.isSingle()) + .filter(expr -> Expression.initialize( + expr, + context.getParsedExpressions().toArray(new Expression[0]), + context.getPatternIndex(), + context.toParseResult())) .map(expr -> (Expression) expr); } else { return Optional.empty(); diff --git a/src/main/java/io/github/syst3ms/skriptparser/registration/context/ContextValues.java b/src/main/java/io/github/syst3ms/skriptparser/registration/context/ContextValues.java index 3e06feff..32ecd5fb 100644 --- a/src/main/java/io/github/syst3ms/skriptparser/registration/context/ContextValues.java +++ b/src/main/java/io/github/syst3ms/skriptparser/registration/context/ContextValues.java @@ -10,7 +10,7 @@ import java.util.stream.Collectors; public class ContextValues { - private static final List> contextValues = new ArrayList<>(); + private static final List> contextValues = new ArrayList<>(); public static void register(SkriptRegistration reg) { contextValues.addAll(reg.getContextValues()); @@ -20,7 +20,7 @@ public static void register(SkriptRegistration reg) { * Returns an unmodifiable list with all the registered context values. * @return a list with all context values */ - public static List> getContextValues() { + public static List> getContextValues() { return Collections.unmodifiableList(contextValues); } @@ -30,7 +30,7 @@ public static void register(SkriptRegistration reg) { * @param ctx the context class * @return a list with the applicable context values */ - public static List> getContextValues(Class ctx) { + public static List> getContextValues(Class ctx) { return contextValues.stream() .filter(val -> val.getContext().isAssignableFrom(ctx)) .filter(val -> !CollectionUtils.contains(val.getExcluded(), val.getContext())) diff --git a/src/test/java/io/github/syst3ms/skriptparser/parsing/SyntaxParserTest.java b/src/test/java/io/github/syst3ms/skriptparser/parsing/SyntaxParserTest.java index 0b605a3f..a19fa878 100644 --- a/src/test/java/io/github/syst3ms/skriptparser/parsing/SyntaxParserTest.java +++ b/src/test/java/io/github/syst3ms/skriptparser/parsing/SyntaxParserTest.java @@ -46,12 +46,14 @@ public class SyntaxParserTest { @Test public void testDefaultExpressions() { + var parserState = new ParserState(); + parserState.setCurrentContexts(Set.of(TestContext.class, TestContext.SubTestContext.class)); /* * Duration type with SimpleLiteral as default expression * Also checks if non-single types accept single default expressions. */ var pattern = PatternParser.parsePattern("look for default expression [%durations?%]", new SkriptLogger()).orElseThrow(AssertionError::new); - var context = new MatchContext(pattern, new ParserState(), new SkriptLogger()); + var context = new MatchContext(pattern, parserState, new SkriptLogger()); assert pattern.match("look for default expression", 0, context) != -1 : "pattern didn't match"; var expressions = context.getParsedExpressions(); @@ -63,7 +65,7 @@ public void testDefaultExpressions() { * String type with EventExpression as default expression */ pattern = PatternParser.parsePattern("another default expression [%string?%]", new SkriptLogger()).orElseThrow(AssertionError::new); - context = new MatchContext(pattern, new ParserState(), new SkriptLogger()); + context = new MatchContext(pattern, parserState, new SkriptLogger()); assert pattern.match("another default expression", 0, context) != -1 : "pattern didn't match"; expressions = context.getParsedExpressions(); diff --git a/src/test/java/io/github/syst3ms/skriptparser/syntax/EvtTest.java b/src/test/java/io/github/syst3ms/skriptparser/syntax/EvtTest.java index 3a17f3ae..65107b72 100644 --- a/src/test/java/io/github/syst3ms/skriptparser/syntax/EvtTest.java +++ b/src/test/java/io/github/syst3ms/skriptparser/syntax/EvtTest.java @@ -25,7 +25,7 @@ public class EvtTest extends SkriptEvent { static { Parser.getMainRegistration() .newEvent(EvtTest.class, "*test [[only] when %=boolean%]") - .setHandledContexts(SubTestContext.class) + .setHandledContexts(TestContext.class, SubTestContext.class) .register(); Parser.getMainRegistration() .newContextValue(SubTestContext.class, String.class, true, "test", __ -> new String[] {"Hello World!"})