Make DSL rule loading work on TeaVM

This commit is contained in:
Riccardo Azzolini 2019-08-09 20:51:31 +02:00
parent e5b64ce218
commit 2b9fb97648
8 changed files with 94 additions and 109 deletions

View File

@ -60,7 +60,15 @@ public interface Platform {
String[] stacktraceToString(Error e); String[] stacktraceToString(Error e);
void loadPlatformRules(); /**
* Determines the list of files containing DSL rules to load.
*
* @return a <code>List</code> of paths of files which contain DSL rules.
* Each <code>String</code> in the returned <code>List</code> can be passed as an argument to
* {@link StorageUtils#getResourceStream(String)} to access the corresponding file's contents.
* @throws IOException if an IO error occurs while getting the list of rule file paths.
*/
List<String> getRuleFilePaths() throws IOException;
void zip(String targetPath, String destinationFilePath, String password); void zip(String targetPath, String destinationFilePath, String password);

View File

@ -1,13 +1,8 @@
package it.cavallium.warppi.math.rules; package it.cavallium.warppi.math.rules;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.stream.Stream;
import it.cavallium.warppi.Engine; import it.cavallium.warppi.Engine;
import it.cavallium.warppi.Platform;
import it.cavallium.warppi.Platform.ConsoleUtils; import it.cavallium.warppi.Platform.ConsoleUtils;
import it.cavallium.warppi.Platform.StorageUtils;
import it.cavallium.warppi.math.Function; import it.cavallium.warppi.math.Function;
import it.cavallium.warppi.math.MathContext; import it.cavallium.warppi.math.MathContext;
import it.cavallium.warppi.math.functions.Expression; import it.cavallium.warppi.math.functions.Expression;
@ -21,6 +16,10 @@ import it.cavallium.warppi.math.solver.MathSolver;
import it.cavallium.warppi.util.Error; import it.cavallium.warppi.util.Error;
import it.unimi.dsi.fastutil.objects.ObjectArrayList; import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import java.io.IOException;
import java.io.InputStream;
import java.util.stream.Stream;
public class RulesManager { public class RulesManager {
public static ObjectArrayList<Rule>[] rules; public static ObjectArrayList<Rule>[] rules;
@ -37,9 +36,6 @@ public class RulesManager {
loadBuiltinRules(); loadBuiltinRules();
if (Engine.getPlatform().isJavascript()) {
Engine.getPlatform().loadPlatformRules();
} else {
try { try {
loadDslRules(); loadDslRules();
} catch (IOException | DslFilesException e) { } catch (IOException | DslFilesException e) {
@ -50,7 +46,6 @@ public class RulesManager {
Engine.getPlatform().exit(1); Engine.getPlatform().exit(1);
} }
} }
}
private static void loadBuiltinRules() { private static void loadBuiltinRules() {
Stream.of( Stream.of(
@ -71,33 +66,29 @@ public class RulesManager {
} }
private static void loadDslRules() throws IOException, DslFilesException { private static void loadDslRules() throws IOException, DslFilesException {
final StorageUtils storageUtils = Engine.getPlatform().getStorageUtils(); final Platform platform = Engine.getPlatform();
final File dslRulesPath = storageUtils.get("rules/dsl/");
if (!dslRulesPath.exists()) {
return;
}
final DslFilesException fileErrors = new DslFilesException(); final DslFilesException fileErrors = new DslFilesException();
for (final File file : storageUtils.walk(dslRulesPath)) { for (final String path : platform.getRuleFilePaths()) {
if (!file.toString().endsWith(".rules")) { platform.getConsoleUtils().out().println(
continue;
}
Engine.getPlatform().getConsoleUtils().out().println(
ConsoleUtils.OUTPUTLEVEL_NODEBUG, ConsoleUtils.OUTPUTLEVEL_NODEBUG,
"RulesManager", "RulesManager",
"Found DSL rules file: " + file.getAbsolutePath() "Found DSL rules file: " + path
); );
final String source; final String source;
try (final InputStream resource = storageUtils.getResourceStream(file.toString())) { try (final InputStream resource = platform.getStorageUtils().getResourceStream(path)) {
source = storageUtils.read(resource); source = platform.getStorageUtils().read(resource);
} }
try { try {
RulesDsl.makeRules(source).forEach(RulesManager::addRule); // This loop used to be written as RulesDsl.makeRules(source).forEach(RulesManager::addRule),
// but the forEach method hangs on TeaVM.
for (Rule rule : RulesDsl.makeRules(source)) {
addRule(rule);
}
} catch (DslAggregateException e) { } catch (DslAggregateException e) {
fileErrors.addFileErrors(file, source, e.getErrors()); fileErrors.addFileErrors(path, source, e.getErrors());
} }
} }

View File

@ -2,7 +2,6 @@ package it.cavallium.warppi.math.rules.dsl.errorutils;
import it.cavallium.warppi.math.rules.dsl.DslError; import it.cavallium.warppi.math.rules.dsl.DslError;
import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -15,13 +14,13 @@ public class DslFilesException extends Exception {
/** /**
* Registers errors which have been found in the specified DSL source file. * Registers errors which have been found in the specified DSL source file.
* *
* @param file The path of the DSL source file in which the errors occurred. * @param filePath The path of the DSL source file in which the errors occurred.
* @param source The entire contents of the DSL source file in which the errors occurred. * @param source The entire contents of the DSL source file in which the errors occurred.
* @param errors The (non-empty) list of errors found in the DSL source file. * @param errors The (non-empty) list of errors found in the DSL source file.
* @throws IllegalArgumentException If the list of errors is empty. * @throws IllegalArgumentException If the list of errors is empty.
*/ */
public void addFileErrors(final File file, final String source, final List<DslError> errors) { public void addFileErrors(final String filePath, final String source, final List<DslError> errors) {
filesErrors.add(new FileErrors(file, source, errors)); filesErrors.add(new FileErrors(filePath, source, errors));
} }
/** /**

View File

@ -2,7 +2,6 @@ package it.cavallium.warppi.math.rules.dsl.errorutils;
import it.cavallium.warppi.math.rules.dsl.DslError; import it.cavallium.warppi.math.rules.dsl.DslError;
import java.io.File;
import java.util.List; import java.util.List;
/** /**
@ -11,23 +10,23 @@ import java.util.List;
* Also stores the file's path and contents (for error reporting). * Also stores the file's path and contents (for error reporting).
*/ */
public class FileErrors { public class FileErrors {
private final File file; private final String filePath;
private final String source; private final String source;
private final List<DslError> errors; private final List<DslError> errors;
/** /**
* Constructs a <code>FileErrors</code> instance with the given file and error data. * Constructs a <code>FileErrors</code> instance with the given file and error data.
* *
* @param file The path of the DSL source file in which the errors occurred. * @param filePath The path of the DSL source file in which the errors occurred.
* @param source The entire contents of the DSL source file in which the errors occurred. * @param source The entire contents of the DSL source file in which the errors occurred.
* @param errors The (non-empty) list of errors found in the DSL source file. * @param errors The (non-empty) list of errors found in the DSL source file.
* @throws IllegalArgumentException If the list of errors is empty. * @throws IllegalArgumentException If the list of errors is empty.
*/ */
public FileErrors(final File file, final String source, final List<DslError> errors) { public FileErrors(final String filePath, final String source, final List<DslError> errors) {
if (errors.isEmpty()) { if (errors.isEmpty()) {
throw new IllegalArgumentException("The list of errors can't be empty"); throw new IllegalArgumentException("The list of errors can't be empty");
} }
this.file = file; this.filePath = filePath;
this.source = source; this.source = source;
this.errors = errors; this.errors = errors;
} }
@ -35,8 +34,8 @@ public class FileErrors {
/** /**
* @return The path of the DSL source file in which the errors occurred. * @return The path of the DSL source file in which the errors occurred.
*/ */
public File getFile() { public String getFilePath() {
return file; return filePath;
} }
/** /**

View File

@ -3,7 +3,6 @@ package it.cavallium.warppi.math.rules.dsl.errorutils;
import it.cavallium.warppi.math.rules.dsl.DslError; import it.cavallium.warppi.math.rules.dsl.DslError;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import java.io.File;
import java.util.Comparator; import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -26,7 +25,7 @@ public class FilesErrorsFormatter {
*/ */
public String format(final List<FileErrors> filesErrors) { public String format(final List<FileErrors> filesErrors) {
return filesErrors.stream() return filesErrors.stream()
.sorted(Comparator.comparing(FileErrors::getFile)) .sorted(Comparator.comparing(FileErrors::getFilePath))
.flatMap(this::formatFileErrors) .flatMap(this::formatFileErrors)
.collect(Collectors.joining(System.lineSeparator())); .collect(Collectors.joining(System.lineSeparator()));
} }
@ -35,17 +34,17 @@ public class FilesErrorsFormatter {
final LineMap lines = new LineMap(fileErrors.getSource()); final LineMap lines = new LineMap(fileErrors.getSource());
return fileErrors.getErrors().stream() return fileErrors.getErrors().stream()
.sorted(Comparator.comparing(DslError::getPosition).thenComparing(DslError::getLength)) .sorted(Comparator.comparing(DslError::getPosition).thenComparing(DslError::getLength))
.map(error -> formatError(fileErrors.getFile(), lines, error)); .map(error -> formatError(fileErrors.getFilePath(), lines, error));
} }
private String formatError(final File file, final LineMap lines, final DslError error) { private String formatError(final String filePath, final LineMap lines, final DslError error) {
final StringBuilder builder = new StringBuilder(); final StringBuilder builder = new StringBuilder();
final List<LineMap.Line> spannedLines = lines.getSpannedLines(error.getPosition(), error.getLength()); final List<LineMap.Line> spannedLines = lines.getSpannedLines(error.getPosition(), error.getLength());
final LineMap.Line firstLine = spannedLines.get(0); final LineMap.Line firstLine = spannedLines.get(0);
final int column = error.getPosition() - firstLine.getStartPosition() + 1; final int column = error.getPosition() - firstLine.getStartPosition() + 1;
builder.append(file.toString()).append(":") builder.append(filePath).append(":")
.append(firstLine.getNumber()).append(":") .append(firstLine.getNumber()).append(":")
.append(column).append(":") .append(column).append(":")
.append(System.lineSeparator()); .append(System.lineSeparator());

View File

@ -5,7 +5,9 @@ import java.io.IOException;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.io.StringWriter; import java.io.StringWriter;
import java.net.URL; import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
@ -157,8 +159,18 @@ public class DesktopPlatform implements Platform {
} }
@Override @Override
public void loadPlatformRules() { public List<String> getRuleFilePaths() throws IOException {
final File dslRulesPath = getStorageUtils().get("rules/dsl/");
List<String> paths = new ArrayList<>();
if (dslRulesPath.exists()) {
for (final File file : getStorageUtils().walk(dslRulesPath)) {
final String path = file.toString();
if (path.endsWith(".rules")) {
paths.add(path);
}
}
}
return paths;
} }
@Override @Override

View File

@ -1,10 +1,13 @@
package it.cavallium.warppi.hardware; package it.cavallium.warppi.hardware;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.io.StringWriter; import java.io.StringWriter;
import java.net.URL; import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import it.cavallium.warppi.Platform; import it.cavallium.warppi.Platform;
@ -146,8 +149,18 @@ public class HardwarePlatform implements Platform {
} }
@Override @Override
public void loadPlatformRules() { public List<String> getRuleFilePaths() throws IOException {
final File dslRulesPath = getStorageUtils().get("rules/dsl/");
List<String> paths = new ArrayList<>();
if (dslRulesPath.exists()) {
for (final File file : getStorageUtils().walk(dslRulesPath)) {
final String path = file.toString();
if (path.endsWith(".rules")) {
paths.add(path);
}
}
}
return paths;
} }
@Override @Override

View File

@ -1,7 +1,10 @@
package it.cavallium.warppi.teavm; package it.cavallium.warppi.teavm;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL; import java.net.URL;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import org.teavm.jso.browser.Window; import org.teavm.jso.browser.Window;
@ -10,7 +13,6 @@ import org.teavm.jso.dom.html.HTMLDocument;
import it.cavallium.warppi.Platform; import it.cavallium.warppi.Platform;
import it.cavallium.warppi.gui.graphicengine.GraphicEngine; import it.cavallium.warppi.gui.graphicengine.GraphicEngine;
import it.cavallium.warppi.gui.graphicengine.html.HtmlEngine; import it.cavallium.warppi.gui.graphicengine.html.HtmlEngine;
import it.cavallium.warppi.math.rules.RulesManager;
import it.cavallium.warppi.util.Error; import it.cavallium.warppi.util.Error;
public class TeaVMPlatform implements Platform { public class TeaVMPlatform implements Platform {
@ -140,57 +142,19 @@ public class TeaVMPlatform implements Platform {
return e.getMessage().toUpperCase().replace("\r", "").split("\n"); return e.getMessage().toUpperCase().replace("\r", "").split("\n");
} }
/**
* Fetches the list of resource files containing DSL rules to load from the <code>/rules.list</code> resource on the server.
* <p>
* The <code>/rules.list</code> resource must exist and be a text file with zero or more lines.
* Each line specifies the name of another resource containing DSL source code.
* Blank lines aren't allowed, and resource names are interpreted exactly as written (without stripping
* leading/trailing spaces, etc.)
*/
@Override @Override
public void loadPlatformRules() { public List<String> getRuleFilePaths() throws IOException {
RulesManager.addRule(new rules.functions.DivisionRule()); try (final InputStream listStream = getStorageUtils().getResourceStream("/rules.list")) {
RulesManager.addRule(new rules.functions.EmptyNumberRule()); return getStorageUtils().readAllLines(listStream);
RulesManager.addRule(new rules.functions.ExpressionRule()); }
RulesManager.addRule(new rules.functions.JokeRule());
RulesManager.addRule(new rules.functions.MultiplicationRule());
RulesManager.addRule(new rules.functions.NegativeRule());
RulesManager.addRule(new rules.functions.NumberRule());
RulesManager.addRule(new rules.functions.PowerRule());
RulesManager.addRule(new rules.functions.RootRule());
RulesManager.addRule(new rules.functions.SubtractionRule());
RulesManager.addRule(new rules.functions.SumRule());
RulesManager.addRule(new rules.functions.SumSubtractionRule());
RulesManager.addRule(new rules.functions.VariableRule());
RulesManager.addRule(new rules.ExpandRule1());
RulesManager.addRule(new rules.ExpandRule2());
RulesManager.addRule(new rules.ExpandRule5());
RulesManager.addRule(new rules.ExponentRule1());
RulesManager.addRule(new rules.ExponentRule2());
RulesManager.addRule(new rules.ExponentRule3());
RulesManager.addRule(new rules.ExponentRule4());
RulesManager.addRule(new rules.ExponentRule8());
RulesManager.addRule(new rules.ExponentRule9());
RulesManager.addRule(new rules.ExponentRule15());
RulesManager.addRule(new rules.ExponentRule16());
RulesManager.addRule(new rules.ExponentRule17());
RulesManager.addRule(new rules.FractionsRule1());
RulesManager.addRule(new rules.FractionsRule2());
RulesManager.addRule(new rules.FractionsRule3());
RulesManager.addRule(new rules.FractionsRule4());
RulesManager.addRule(new rules.FractionsRule5());
RulesManager.addRule(new rules.FractionsRule6());
RulesManager.addRule(new rules.FractionsRule7());
RulesManager.addRule(new rules.FractionsRule8());
RulesManager.addRule(new rules.FractionsRule9());
RulesManager.addRule(new rules.FractionsRule10());
RulesManager.addRule(new rules.FractionsRule11());
RulesManager.addRule(new rules.FractionsRule12());
RulesManager.addRule(new rules.FractionsRule14());
RulesManager.addRule(new rules.NumberRule1());
RulesManager.addRule(new rules.NumberRule2());
RulesManager.addRule(new rules.NumberRule3());
RulesManager.addRule(new rules.NumberRule4());
RulesManager.addRule(new rules.NumberRule5());
RulesManager.addRule(new rules.NumberRule7());
RulesManager.addRule(new rules.UndefinedRule1());
RulesManager.addRule(new rules.UndefinedRule2());
RulesManager.addRule(new rules.VariableRule1());
RulesManager.addRule(new rules.VariableRule2());
RulesManager.addRule(new rules.VariableRule3());
} }
@Override @Override