Skip to content

Commit

Permalink
Feature/entry sections (#126)
Browse files Browse the repository at this point in the history
* ✨ CategorySection

* ✨ SectionConfiguration

* ✨ EntryLoader (to customize the parsing process)

* ✨ LiteralLoader (to add literal values)

* 📝 EntryLoader (Javadoc)

* SectionConfiguration: refactored a lot of code, fixed issues, improved error messages

* SectionLoader: small change

---------

Co-authored-by: Mwexim <[email protected]>
  • Loading branch information
Mwexim and Mwexim authored Oct 31, 2023
1 parent a7ce31f commit 838ed82
Show file tree
Hide file tree
Showing 12 changed files with 520 additions and 6 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package io.github.syst3ms.skriptparser.lang;

import io.github.syst3ms.skriptparser.file.FileSection;
import io.github.syst3ms.skriptparser.log.SkriptLogger;
import io.github.syst3ms.skriptparser.parsing.ParseContext;
import io.github.syst3ms.skriptparser.parsing.ParserState;
import org.jetbrains.annotations.Contract;

import java.util.Optional;

public class SimpleCodeSection extends CodeSection {
private final String name;

public SimpleCodeSection(FileSection section, ParserState parserState, SkriptLogger logger, String name) {
this.name = name;
loadSection(section, parserState, logger);
}

@Override
@Contract("_, _, _ -> fail")
public boolean init(Expression<?>[] expressions, int matchedPattern, ParseContext parseContext) {
throw new UnsupportedOperationException();
}

@Override
public Optional<? extends Statement> walk(TriggerContext ctx) {
return getFirst();
}

@Override
public String toString(TriggerContext ctx, boolean debug) {
return name;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,14 @@
* </ul>
*/
public abstract class SkriptEvent implements SyntaxElement {

/**
* Whether this event should trigger, given the {@link TriggerContext}
* @param ctx the TriggerContext to check
* @return whether the event should trigger
*/
public abstract boolean check(TriggerContext ctx);

List<Statement> loadSection(FileSection section, ParserState parserState, SkriptLogger logger) {
public List<Statement> loadSection(FileSection section, ParserState parserState, SkriptLogger logger) {
return ScriptLoader.loadItems(section, parserState, logger);
}

Expand All @@ -52,7 +51,7 @@ public int getLoadingPriority() {

/**
* A list of the classes of every syntax that is allowed to be used inside of this SkriptEvent. The default behavior
* is to return an empty list, which equates to no restrictions. If overriden, this allows the creation of specialized,
* is to return an empty list, which equates to no restrictions. If overridden, this allows the creation of specialized,
* 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
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package io.github.syst3ms.skriptparser.lang.entries;

import io.github.syst3ms.skriptparser.file.FileElement;
import io.github.syst3ms.skriptparser.log.SkriptLogger;
import io.github.syst3ms.skriptparser.parsing.ParserState;

public abstract class EntryLoader {
protected final String key;
private final boolean optional;

public EntryLoader(String key, boolean optional) {
this.key = key;
this.optional = optional;
}

/**
* This {@link EntryLoader} will attempt to load the entry
* using its {@linkplain FileElement}. One can use this method
* to create specific error messages or to load the value correctly.
* <br>
* If an error occurred while executing this method, meaning {@linkplain SkriptLogger#hasError()}
* is {@code true}, the parser will not loop over the other {@linkplain FileElement elements}
* present. If this method returned {@code false}, but there is no error given,
* a default error message {@code "The entry has not been configured."} will be used.
* @param config the configuration
* @param element the element
* @param parserState the parser state
* @param logger the logger
* @return {@code true} if loaded successfully, {@code false} if an error occurred
*/
public abstract boolean loadEntry(SectionConfiguration config, FileElement element, ParserState parserState, SkriptLogger logger);

public boolean isOptional() {
return optional;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package io.github.syst3ms.skriptparser.lang.entries;

import io.github.syst3ms.skriptparser.file.FileElement;
import io.github.syst3ms.skriptparser.log.ErrorType;
import io.github.syst3ms.skriptparser.log.SkriptLogger;
import io.github.syst3ms.skriptparser.parsing.ParserState;
import io.github.syst3ms.skriptparser.types.TypeManager;

import java.util.ArrayList;
import java.util.List;

public class LiteralLoader<T> extends OptionLoader {
private final Class<T> typeClass;

public LiteralLoader(String key, Class<T> typeClass, boolean multiple, boolean optional) {
super(key, multiple, optional);
this.typeClass = typeClass;
}

@Override
public boolean loadEntry(SectionConfiguration config, FileElement element, ParserState parserState, SkriptLogger logger) {
// We will use the loaded values later
if (!super.loadEntry(config, element, parserState, logger))
return false;

var type = TypeManager.getByClassExact(typeClass);
if (type.isEmpty()) {
logger.error("Couldn't find a type corresponding to the class '" + typeClass.getName() + "'", ErrorType.NO_MATCH);
return false;
} else if (type.get().getLiteralParser().isEmpty()) {
logger.error("The type '" + type.get().getBaseName() + "' doesn't have a literal parser.", ErrorType.NO_MATCH);
return false;
}
var parser = type.get().getLiteralParser().get();

logger.setLine(element.getLine() - 1);

if (isMultiple()) {
List<T> data = new ArrayList<>();
boolean successful = true;
for (var value : config.getStringList(key)) {
var result = parser.apply(value);
if (result == null) {
// With the logic that errors get skipped, we will allow the other values to be parsed.
logger.error("Couldn't parse '" + value + "' as " + type.get().withIndefiniteArticle(false), ErrorType.SEMANTIC_ERROR);
logger.finalizeLogs();
successful = false;
continue;
}
data.add(result);
}
config.getData().put(key, data.toArray());
return successful;
} else {
var result = parser.apply(config.getString(key));
// We don't want this data to linger if an error occurs
config.getData().remove(key);
if (result == null) {
logger.error("Couldn't parse '" + config.getString(key) + "' as " + type.get().withIndefiniteArticle(false), ErrorType.SEMANTIC_ERROR);
return false;
}
config.getData().put(key, result);
return true;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package io.github.syst3ms.skriptparser.lang.entries;

import io.github.syst3ms.skriptparser.file.FileElement;
import io.github.syst3ms.skriptparser.file.FileSection;
import io.github.syst3ms.skriptparser.file.VoidElement;
import io.github.syst3ms.skriptparser.log.ErrorType;
import io.github.syst3ms.skriptparser.log.SkriptLogger;
import io.github.syst3ms.skriptparser.parsing.ParserState;

public class OptionLoader extends EntryLoader {
public static final String OPTION_SPLIT_PATTERN = ": ";

private final boolean multiple;

public OptionLoader(String key, boolean multiple, boolean optional) {
super(key, optional);
this.multiple = multiple;
}

@Override
public boolean loadEntry(SectionConfiguration config, FileElement element, ParserState parserState, SkriptLogger logger) {
var content = element.getLineContent().split(OPTION_SPLIT_PATTERN);
if (content.length == 0)
return false;
var key = content[0];
var entry = content.length > 1 ? content[1] : null;

if (!key.equalsIgnoreCase(this.key))
return false;
if (element instanceof FileSection) {
if (!multiple) {
logger.error("The entry '" + key + "' does not support multiple values.", ErrorType.SEMANTIC_ERROR);
return false;
} else if (entry != null) {
logger.error("The entry '" + key + "' has been configured incorrectly.", ErrorType.SEMANTIC_ERROR);
return false;
}
config.getData().put(this.key, ((FileSection) element).getElements().stream()
.filter(el -> !(el instanceof VoidElement))
.map(FileElement::getLineContent)
.toArray(String[]::new)
);
} else {
if (entry == null) {
logger.error("The entry '" + key + "' has been configured incorrectly.", ErrorType.SEMANTIC_ERROR);
return false;
}
config.getData().put(this.key, multiple ? new String[] {entry} : entry);
}
return true;
}

public boolean isMultiple() {
return multiple;
}
}
Loading

0 comments on commit 838ed82

Please sign in to comment.