Skip to content
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

Functions & Structures 🎉 #156

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 5 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
695 changes: 674 additions & 21 deletions LICENSE

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
plugins {
id "com.github.johnrengelman.shadow" version "8.1.1"
id "java"
id "application"
}

mainClassName = "io.github.syst3ms.skriptparser.Parser"

sourceCompatibility = 1.11
sourceCompatibility = 1.17

repositories {
mavenCentral()
Expand Down
5 changes: 4 additions & 1 deletion src/main/java/io/github/syst3ms/skriptparser/Parser.java
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ public static void init(String[] mainPackages, String[] subPackages, String[] pr
mainPackages[mainPackages.length - 1] = "io.github.syst3ms.skriptparser";
List<String> sub = new ArrayList<>();
sub.addAll(Arrays.asList(subPackages));
sub.addAll(Arrays.asList("expressions", "effects", "event", "lang", "sections", "tags"));
sub.addAll(Arrays.asList("expressions", "effects", "event", "lang", "sections", "structures", "javafunctions", "tags"));
subPackages = sub.toArray(new String[0]);
try {
for (String mainPackage : mainPackages) {
Expand Down Expand Up @@ -138,7 +138,10 @@ public static void init(String[] mainPackages, String[] subPackages, String[] pr
public static void run(String scriptName, boolean debug, boolean tipsEnabled) {
Calendar time = Calendar.getInstance();
Path scriptPath = Paths.get(scriptName);
long start = System.currentTimeMillis();
logs = ScriptLoader.loadScript(scriptPath, debug);
long elapsed = System.currentTimeMillis()-start;
System.out.println("Script \"" + scriptName + "\" has been parsed in " + elapsed + "ms");
if (!logs.isEmpty()) {
System.out.print(ConsoleColors.PURPLE);
System.out.println("Parsing log:");
Expand Down
13 changes: 5 additions & 8 deletions src/main/java/io/github/syst3ms/skriptparser/Skript.java
Original file line number Diff line number Diff line change
@@ -1,17 +1,11 @@
package io.github.syst3ms.skriptparser;

import io.github.syst3ms.skriptparser.event.AtTimeContext;
import io.github.syst3ms.skriptparser.event.EvtAtTime;
import io.github.syst3ms.skriptparser.event.EvtPeriodical;
import io.github.syst3ms.skriptparser.event.EvtScriptLoad;
import io.github.syst3ms.skriptparser.event.EvtWhen;
import io.github.syst3ms.skriptparser.event.PeriodicalContext;
import io.github.syst3ms.skriptparser.event.ScriptLoadContext;
import io.github.syst3ms.skriptparser.event.WhenContext;
import io.github.syst3ms.skriptparser.event.*;
import io.github.syst3ms.skriptparser.lang.SkriptEvent;
import io.github.syst3ms.skriptparser.lang.Statement;
import io.github.syst3ms.skriptparser.lang.Trigger;
import io.github.syst3ms.skriptparser.registration.SkriptAddon;
import io.github.syst3ms.skriptparser.structures.functions.StructFunction;
import io.github.syst3ms.skriptparser.util.DurationUtils;
import io.github.syst3ms.skriptparser.util.ThreadUtils;
import io.github.syst3ms.skriptparser.util.Time;
Expand Down Expand Up @@ -51,6 +45,8 @@ public void handleTrigger(Trigger trigger) {
whenTriggers.add(trigger);
} else if (event instanceof EvtAtTime) {
atTimeTriggers.add(trigger);
} else if (event instanceof StructFunction function) {
function.register(trigger);
}
}

Expand All @@ -77,4 +73,5 @@ public void finishedLoading() {
ThreadUtils.runPeriodically(() -> Statement.runAll(trigger, ctx), initialDelay, Duration.ofDays(1));
}
}

}
83 changes: 54 additions & 29 deletions src/main/java/io/github/syst3ms/skriptparser/effects/EffReturn.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
import io.github.syst3ms.skriptparser.log.ErrorType;
import io.github.syst3ms.skriptparser.parsing.ParseContext;
import io.github.syst3ms.skriptparser.parsing.SkriptParserException;
import io.github.syst3ms.skriptparser.structures.functions.Function;
import io.github.syst3ms.skriptparser.structures.functions.FunctionContext;
import io.github.syst3ms.skriptparser.types.TypeManager;
import io.github.syst3ms.skriptparser.types.conversions.Converters;
import io.github.syst3ms.skriptparser.util.StringUtils;
Expand All @@ -31,6 +33,7 @@ public class EffReturn extends Effect {
"return %objects%"
);
}
private boolean isInFunction;

private ReturnSection<?> section;
private Expression<?> returned;
Expand All @@ -39,46 +42,68 @@ public class EffReturn extends Effect {
public boolean init(Expression<?>[] expressions, int matchedPattern, ParseContext parseContext) {
returned = expressions[0];
var logger = parseContext.getLogger();
var sec = Expression.getLinkedSection(parseContext.getParserState(), ReturnSection.class);
if (sec.isEmpty()) {
logger.error("Couldn't find a section matching this return statement", ErrorType.SEMANTIC_ERROR);
return false;
}
section = (ReturnSection<?>) sec.get();
if (section.isSingle() && !returned.isSingle()) {
logger.error("Only a single return value was expected, but multiple were given", ErrorType.SEMANTIC_ERROR);
return false;
} else if (!Converters.converterExists(returned.getReturnType(), section.getReturnType())) {
var secType = TypeManager.getByClass(section.getReturnType())
.map(t -> StringUtils.withIndefiniteArticle(t.toString(), false))
.orElse(section.getReturnType().getName());
var exprType = TypeManager.getByClass(returned.getReturnType())
.map(t -> StringUtils.withIndefiniteArticle(t.toString(), false))
.orElseThrow(AssertionError::new);
logger.error(
"Expected " +
secType +
" return value, but found " +
exprType,
ErrorType.SEMANTIC_ERROR
);
return false;
}
if (!section.getReturnType().isAssignableFrom(returned.getReturnType())) {
// The value is convertible but not in the trivial way
returned = returned.convertExpression(section.getReturnType())
.orElseThrow(() -> new SkriptParserException("Return value should be convertible at this stage"));
Optional<Class<? extends TriggerContext>> optionalContext = parseContext.getParserState().getCurrentContexts().stream().findFirst();
if (optionalContext.isPresent()) {
Class<? extends TriggerContext> currentContext = optionalContext.get();
if (currentContext.equals(FunctionContext.class)) {
isInFunction = true;
// cannot verify
} else {
var sec = Expression.getLinkedSection(parseContext.getParserState(), ReturnSection.class);
if (sec.isEmpty()) {
logger.error("Couldn't find a section matching this return statement", ErrorType.SEMANTIC_ERROR);
return false;
}
section = (ReturnSection<?>) sec.get();
if (section.isSingle() && !returned.isSingle()) {
logger.error("Only a single return value was expected, but multiple were given", ErrorType.SEMANTIC_ERROR);
return false;
} else if (!Converters.converterExists(returned.getReturnType(), section.getReturnType())) {
var secType = TypeManager.getByClass(section.getReturnType())
.map(t -> StringUtils.withIndefiniteArticle(t.toString(), false))
.orElse(section.getReturnType().getName());
var exprType = TypeManager.getByClass(returned.getReturnType())
.map(t -> StringUtils.withIndefiniteArticle(t.toString(), false))
.orElseThrow(AssertionError::new);
logger.error(
"Expected " +
secType +
" return value, but found " +
exprType,
ErrorType.SEMANTIC_ERROR
);
return false;
}
if (!section.getReturnType().isAssignableFrom(returned.getReturnType())) {
// The value is convertible but not in the trivial way
returned = returned.convertExpression(section.getReturnType())
.orElseThrow(() -> new SkriptParserException("Return value should be convertible at this stage"));
}
}
} else {

}
return true;
}

@Override
protected void execute(TriggerContext ctx) {
/*if (isInFunction) {
FunctionContext functionContext = (FunctionContext) ctx;
Function<?> function = functionContext.getOwningFunction();
function.setReturnValue(returned.getValues(ctx));
} else throw new UnsupportedOperationException();*/
throw new UnsupportedOperationException();
}

@Override
public Optional<? extends Statement> walk(TriggerContext ctx) {
if (isInFunction) {
FunctionContext functionContext = (FunctionContext) ctx;
Function<?> function = functionContext.getOwningFunction();
function.setReturnValue(returned.getValues(ctx));
return Optional.empty(); // stop the trigger
}
section.setReturned(returned.getValues(ctx));
section.step(this);
return Optional.of(section);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package io.github.syst3ms.skriptparser.javafunctions;

import io.github.syst3ms.skriptparser.structures.functions.FunctionParameter;
import io.github.syst3ms.skriptparser.structures.functions.Functions;
import io.github.syst3ms.skriptparser.structures.functions.JavaFunction;

import java.math.BigDecimal;
import java.math.BigInteger;

public class DefaultFunctions {

static {
Functions.registerFunction(new JavaFunction<>(
"mod",
new FunctionParameter[]{new FunctionParameter<>("d", BigInteger.class, true), new FunctionParameter<>("m", BigInteger.class, true)},
Number.class,
true) {
@Override
public Number[] executeSimple(Object[][] params) {
Number d = (Number) params[0][0];
Number m = (Number) params[1][0];
double mm = m.doubleValue();
if (mm == 0)
return new Number[] {BigDecimal.valueOf(Double.NaN)};
return new Number[] {BigDecimal.valueOf((d.doubleValue() % mm + mm) % mm)};
}
});
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,12 @@ public List<Statement> loadSection(FileSection section, ParserState parserState,
* This is undesirable if we don't want the restriction of having to declare functions before using them. This is especially
* counter-productive if we're dealing with multiple scripts.
*
* To solve this problem, {@link Trigger triggers} with with a higher loading priority number will be loaded first.
* To solve this problem, {@link Trigger triggers} with a higher loading priority number will be loaded first.
*
* @return the loading priority number. 0 by default
*/
public int getLoadingPriority() {
return 0;
return 500;
}

/**
Expand All @@ -55,6 +55,7 @@ public int getLoadingPriority() {
* DSL-like sections in which only select {@linkplain Statement statements} and other {@linkplain CodeSection sections}
* (and potentially, but not necessarily, expressions).
* @return a list of the classes of each syntax allowed inside this SkriptEvent
* or {@code null} if you don't want to allow any
* @see #isRestrictingExpressions()
*/
protected Set<Class<? extends SyntaxElement>> getAllowedSyntaxes() {
Expand Down
38 changes: 38 additions & 0 deletions src/main/java/io/github/syst3ms/skriptparser/lang/Structure.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package io.github.syst3ms.skriptparser.lang;

import io.github.syst3ms.skriptparser.file.FileElement;
import io.github.syst3ms.skriptparser.file.FileSection;
import io.github.syst3ms.skriptparser.lang.entries.SectionConfiguration;
import io.github.syst3ms.skriptparser.log.SkriptLogger;
import io.github.syst3ms.skriptparser.parsing.ParserState;
import io.github.syst3ms.skriptparser.parsing.ScriptLoader;

import java.util.List;

public abstract class Structure extends SkriptEvent {


protected SectionConfiguration getConfiguration() {
return null;
}

@Override
public List<Statement> loadSection(FileSection section, ParserState parserState, SkriptLogger logger) {
SectionConfiguration configuration = getConfiguration();
if (configuration != null) {
configuration.loadConfiguration(null, section, parserState, logger);
List<FileElement> elements = section.getElements();
elements.subList(0, configuration.getEntries().size()).clear();
}
return ScriptLoader.loadItems(section, parserState, logger);
}

/**
* @return the default loading priority for structures
*/
@Override
public int getLoadingPriority() {
return 400;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,10 @@ public Map<String, Object> getData() {
return data;
}

public List<EntryLoader> getEntries() {
return entries;
}

public Object getValue(String key) {
return data.get(key);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public class ParserState {
private final LinkedList<CodeSection> currentSections = new LinkedList<>();
private final LinkedList<LinkedList<Statement>> currentStatements = new LinkedList<>();
private final LinkedList<Pair<Set<Class<? extends SyntaxElement>>, Boolean>> restrictions = new LinkedList<>();
private boolean isntAllowingSyntax = false;

{
currentStatements.add(new LinkedList<>());
Expand Down Expand Up @@ -103,13 +104,15 @@ public void callbackCurrentStatements() {
* @param restrictingExpressions whether expressions are also restricted
*/
public void setSyntaxRestrictions(Set<Class<? extends SyntaxElement>> allowedSyntaxes, boolean restrictingExpressions) {
if (allowedSyntaxes == null) isntAllowingSyntax = true;
restrictions.addLast(new Pair<>(allowedSyntaxes, restrictingExpressions));
}

/**
* Clears the previously enforced syntax restrictions
*/
public void clearSyntaxRestrictions() {
isntAllowingSyntax = false;
restrictions.removeLast();
}

Expand All @@ -118,6 +121,7 @@ public void clearSyntaxRestrictions() {
* @return whether the current syntax restrictions forbid a given syntax or not
*/
public boolean forbidsSyntax(Class<? extends SyntaxElement> c) {
if (isntAllowingSyntax) return true;
var allowedSyntaxes = restrictions.getLast().getFirst();
return !allowedSyntaxes.isEmpty() && !allowedSyntaxes.contains(c);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import io.github.syst3ms.skriptparser.file.FileParser;
import io.github.syst3ms.skriptparser.file.FileSection;
import io.github.syst3ms.skriptparser.file.VoidElement;
import io.github.syst3ms.skriptparser.lang.SkriptEvent;
import io.github.syst3ms.skriptparser.lang.Statement;
import io.github.syst3ms.skriptparser.lang.Trigger;
import io.github.syst3ms.skriptparser.lang.UnloadedTrigger;
Expand Down Expand Up @@ -73,6 +74,13 @@ public static List<LogEntry> loadScript(Path scriptPath, SkriptLogger logger, bo
trig.ifPresent(t -> {
logger.setLine(logger.getLine() + ((FileSection) element).length());
unloadedTriggers.add(t);
//SkriptEvent skriptEvent = t.getTrigger().getEvent();
/* TODO
validate that this is a function
parse & save it
have a separate parser to parse a whole folder that does this step before anything
*/
//System.out.println(skriptEvent);
});
} else {
logger.error(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ public static <T> Optional<? extends Expression<? extends T>> parseExpression(St
if (expr.isPresent()) {
if (parserState.isRestrictingExpressions() && parserState.forbidsSyntax(expr.get().getClass())) {
logger.setContext(ErrorContext.RESTRICTED_SYNTAXES);
logger.error("The enclosing section does not allow the use of this expression: " + expr.get().toString(TriggerContext.DUMMY, logger.isDebug()), ErrorType.SEMANTIC_ERROR);
logger.error("The enclosing code section does not allow the use of this expression: " + expr.get().toString(TriggerContext.DUMMY, logger.isDebug()), ErrorType.SEMANTIC_ERROR);
return Optional.empty();
}
recentExpressions.acknowledge(info);
Expand Down Expand Up @@ -329,7 +329,7 @@ public static Optional<? extends Expression<Boolean>> parseBooleanExpression(Str
return Optional.empty();
} else if (parserState.isRestrictingExpressions() && parserState.forbidsSyntax(ContextExpression.class)) {
logger.setContext(ErrorContext.RESTRICTED_SYNTAXES);
logger.error("The enclosing section does not allow the use of context expressions.", ErrorType.SEMANTIC_ERROR);
logger.error("The enclosing code section does not allow the use of context expressions.", ErrorType.SEMANTIC_ERROR);
return Optional.empty();
}

Expand Down Expand Up @@ -392,7 +392,7 @@ private static <T> Optional<? extends Expression<? extends T>> matchExpressionIn
if (parserState.isRestrictingExpressions() && parserState.forbidsSyntax(expression.getClass())) {
logger.setContext(ErrorContext.RESTRICTED_SYNTAXES);
logger.error(
"The enclosing section does not allow the use of this expression: "
"The enclosing code section does not allow the use of this expression: "
+ expression.toString(TriggerContext.DUMMY, logger.isDebug()),
ErrorType.SEMANTIC_ERROR,
"The current section limits the usage of syntax. This means that certain syntax cannot be used here, which was the case. Remove this expression entirely and refer to the documentation for the correct usage of this section"
Expand Down Expand Up @@ -566,7 +566,7 @@ public static Optional<? extends Effect> parseEffect(String s, ParserState parse
if (eff.isPresent()) {
if (parserState.forbidsSyntax(eff.get().getClass())) {
logger.setContext(ErrorContext.RESTRICTED_SYNTAXES);
logger.error("The enclosing section does not allow the use of this effect: " + eff.get().toString(TriggerContext.DUMMY, logger.isDebug()), ErrorType.SEMANTIC_ERROR);
logger.error("The enclosing code section does not allow the use of this effect: " + eff.get().toString(TriggerContext.DUMMY, logger.isDebug()), ErrorType.SEMANTIC_ERROR);
return Optional.empty();
}
recentEffects.acknowledge(recentEffect);
Expand Down Expand Up @@ -627,7 +627,7 @@ public static Optional<? extends CodeSection> parseSection(FileSection section,
if (sec.isPresent()) {
if (parserState.forbidsSyntax(sec.get().getClass())) {
logger.setContext(ErrorContext.RESTRICTED_SYNTAXES);
logger.error("The enclosing section does not allow the use of this section: " + sec.get().toString(TriggerContext.DUMMY, logger.isDebug()), ErrorType.SEMANTIC_ERROR);
logger.error("The enclosing code section does not allow the use of this section: " + sec.get().toString(TriggerContext.DUMMY, logger.isDebug()), ErrorType.SEMANTIC_ERROR);
return Optional.empty();
}
recentSections.acknowledge(toParse);
Expand Down
Loading