mirror of
https://github.com/aNNiMON/HotaruFX.git
synced 2024-09-19 14:14:21 +03:00
Update to run on Java 11 with OpenJFX
Removed lombok
This commit is contained in:
parent
eb6bf422cc
commit
93e7a87c03
@ -1,26 +1,33 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id 'com.github.johnrengelman.shadow' version '2.0.1'
|
|
||||||
id 'java'
|
id 'java'
|
||||||
id 'application'
|
id 'application'
|
||||||
|
id "com.github.johnrengelman.shadow" version "5.1.0"
|
||||||
|
id 'org.openjfx.javafxplugin' version '0.0.8'
|
||||||
}
|
}
|
||||||
|
|
||||||
group 'HotaruFX'
|
group 'HotaruFX'
|
||||||
version '0.9.1'
|
version '1.0.0'
|
||||||
mainClassName = 'com.annimon.hotarufx.Main'
|
mainClassName = 'com.annimon.hotarufx.Main'
|
||||||
|
|
||||||
sourceCompatibility = 1.8
|
sourceCompatibility = 11
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
javafx {
|
||||||
|
version = "11"
|
||||||
|
modules = ['javafx.controls', 'javafx.fxml', "javafx.swing"]
|
||||||
|
}
|
||||||
|
|
||||||
|
ext.junit5Version = '5.5.2'
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'org.fxmisc.richtext:richtextfx:0.6.10'
|
implementation 'org.fxmisc.richtext:richtextfx:0.10.2'
|
||||||
compileOnly 'org.projectlombok:lombok:1.16.18'
|
testRuntime 'org.junit.platform:junit-platform-launcher:1.5.2'
|
||||||
testCompileOnly 'org.projectlombok:lombok:1.16.18'
|
testImplementation "org.junit.jupiter:junit-jupiter-api:$junit5Version"
|
||||||
testRuntime 'org.junit.platform:junit-platform-launcher:1.0.0'
|
testImplementation "org.junit.jupiter:junit-jupiter-params:$junit5Version"
|
||||||
testRuntime 'org.junit.jupiter:junit-jupiter-engine:5.0.0'
|
testRuntime "org.junit.jupiter:junit-jupiter-engine:$junit5Version"
|
||||||
testRuntime 'org.junit.vintage:junit-vintage-engine:4.12.0'
|
testRuntime "org.junit.vintage:junit-vintage-engine:$junit5Version"
|
||||||
testRuntime 'org.junit.jupiter:junit-jupiter-params:5.0.0'
|
|
||||||
testImplementation 'org.hamcrest:hamcrest-library:1.3'
|
testImplementation 'org.hamcrest:hamcrest-library:1.3'
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,6 @@ import javafx.fxml.FXMLLoader;
|
|||||||
import javafx.scene.Scene;
|
import javafx.scene.Scene;
|
||||||
import javafx.scene.control.TextArea;
|
import javafx.scene.control.TextArea;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
import lombok.val;
|
|
||||||
|
|
||||||
public class Main extends Application {
|
public class Main extends Application {
|
||||||
|
|
||||||
@ -20,8 +19,8 @@ public class Main extends Application {
|
|||||||
ClickableHyperLink.setHostServices(getHostServices());
|
ClickableHyperLink.setHostServices(getHostServices());
|
||||||
primaryStage.setTitle("HotaruFX");
|
primaryStage.setTitle("HotaruFX");
|
||||||
try {
|
try {
|
||||||
val loader = new FXMLLoader(getClass().getResource("/fxml/Editor.fxml"));
|
final var loader = new FXMLLoader(getClass().getResource("/fxml/Editor.fxml"));
|
||||||
val scene = new Scene(loader.load());
|
final var scene = new Scene(loader.load());
|
||||||
scene.getStylesheets().addAll(
|
scene.getStylesheets().addAll(
|
||||||
getClass().getResource("/styles/theme-dark.css").toExternalForm(),
|
getClass().getResource("/styles/theme-dark.css").toExternalForm(),
|
||||||
getClass().getResource("/styles/codearea.css").toExternalForm(),
|
getClass().getResource("/styles/codearea.css").toExternalForm(),
|
||||||
@ -33,7 +32,7 @@ public class Main extends Application {
|
|||||||
primaryStage.setOnCloseRequest(controller::onCloseRequest);
|
primaryStage.setOnCloseRequest(controller::onCloseRequest);
|
||||||
primaryStage.setScene(scene);
|
primaryStage.setScene(scene);
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
val text = new TextArea(Exceptions.stackTraceToString(ex));
|
final var text = new TextArea(Exceptions.stackTraceToString(ex));
|
||||||
text.setEditable(false);
|
text.setEditable(false);
|
||||||
primaryStage.setScene(new Scene(text));
|
primaryStage.setScene(new Scene(text));
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
package com.annimon.hotarufx.bundles;
|
package com.annimon.hotarufx.bundles;
|
||||||
|
|
||||||
import com.annimon.hotarufx.lib.Context;
|
import com.annimon.hotarufx.lib.Context;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
import lombok.val;
|
|
||||||
|
|
||||||
public final class BundleLoader {
|
public final class BundleLoader {
|
||||||
|
|
||||||
@ -26,7 +26,7 @@ public final class BundleLoader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static Map<String, FunctionType> functions() {
|
public static Map<String, FunctionType> functions() {
|
||||||
val functions = new HashMap<String, FunctionType>();
|
final var functions = new HashMap<String, FunctionType>();
|
||||||
apply(runtimeBundles(), functions, ((bundle, map) -> map.putAll(bundle.functions())));
|
apply(runtimeBundles(), functions, ((bundle, map) -> map.putAll(bundle.functions())));
|
||||||
return functions;
|
return functions;
|
||||||
}
|
}
|
||||||
@ -43,9 +43,11 @@ public final class BundleLoader {
|
|||||||
|
|
||||||
for (Class<? extends Bundle> clazz : bundles) {
|
for (Class<? extends Bundle> clazz : bundles) {
|
||||||
try {
|
try {
|
||||||
val bundle = clazz.newInstance();
|
final var ctor = clazz.getDeclaredConstructor();
|
||||||
|
final var bundle = ctor.newInstance();
|
||||||
action.accept(bundle, obj);
|
action.accept(bundle, obj);
|
||||||
} catch (IllegalAccessException | InstantiationException ignore) {}
|
} catch (IllegalAccessException | InstantiationException
|
||||||
|
| NoSuchMethodException | InvocationTargetException ignore) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,6 @@ import java.util.Arrays;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import javafx.scene.paint.Paint;
|
import javafx.scene.paint.Paint;
|
||||||
import lombok.val;
|
|
||||||
import static com.annimon.hotarufx.bundles.FunctionInfo.of;
|
import static com.annimon.hotarufx.bundles.FunctionInfo.of;
|
||||||
import static com.annimon.hotarufx.bundles.FunctionType.COMMON;
|
import static com.annimon.hotarufx.bundles.FunctionType.COMMON;
|
||||||
|
|
||||||
@ -57,11 +56,11 @@ public class CompositionBundle implements Bundle {
|
|||||||
width = args[0].asInt();
|
width = args[0].asInt();
|
||||||
height = args[1].asInt();
|
height = args[1].asInt();
|
||||||
frameRate = args[2].asDouble();
|
frameRate = args[2].asDouble();
|
||||||
val background = PropertyType.PAINT.<Paint>getFromHFX().apply(args[3]);
|
final var background = PropertyType.PAINT.<Paint>getFromHFX().apply(args[3]);
|
||||||
composition = new Composition(width, height, frameRate, background);
|
composition = new Composition(width, height, frameRate, background);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
val scene = composition.getScene();
|
final var scene = composition.getScene();
|
||||||
context.composition(composition);
|
context.composition(composition);
|
||||||
context.variables().put("Width", NumberValue.of(scene.getVirtualWidth()));
|
context.variables().put("Width", NumberValue.of(scene.getVirtualWidth()));
|
||||||
context.variables().put("Height", NumberValue.of(scene.getVirtualHeight()));
|
context.variables().put("Height", NumberValue.of(scene.getVirtualHeight()));
|
||||||
@ -73,8 +72,8 @@ public class CompositionBundle implements Bundle {
|
|||||||
|
|
||||||
private static Function render(Context context) {
|
private static Function render(Context context) {
|
||||||
return args -> {
|
return args -> {
|
||||||
val renderVisitor = new RenderVisitor(context.composition().getTimeline());
|
final var renderVisitor = new RenderVisitor(context.composition().getTimeline());
|
||||||
val scene = context.composition().getScene();
|
final var scene = context.composition().getScene();
|
||||||
Arrays.stream(args)
|
Arrays.stream(args)
|
||||||
.filter(v -> v.type() == Types.NODE)
|
.filter(v -> v.type() == Types.NODE)
|
||||||
.map(v -> ((NodeValue) v).getNode())
|
.map(v -> ((NodeValue) v).getNode())
|
||||||
|
@ -9,7 +9,6 @@ import com.annimon.hotarufx.lib.Validator;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import javafx.scene.text.Font;
|
import javafx.scene.text.Font;
|
||||||
import lombok.val;
|
|
||||||
import static com.annimon.hotarufx.bundles.FunctionInfo.of;
|
import static com.annimon.hotarufx.bundles.FunctionInfo.of;
|
||||||
import static com.annimon.hotarufx.bundles.FunctionType.COMMON;
|
import static com.annimon.hotarufx.bundles.FunctionType.COMMON;
|
||||||
|
|
||||||
@ -28,7 +27,7 @@ public class FontBundle implements Bundle {
|
|||||||
|
|
||||||
private static Function newFont(Context context) {
|
private static Function newFont(Context context) {
|
||||||
return args -> {
|
return args -> {
|
||||||
val validator = Validator.with(args);
|
final var validator = Validator.with(args);
|
||||||
validator.check(1);
|
validator.check(1);
|
||||||
if (args[0].type() == Types.MAP) {
|
if (args[0].type() == Types.MAP) {
|
||||||
return new FontValue(FontValue.toFont((MapValue) args[0]));
|
return new FontValue(FontValue.toFont((MapValue) args[0]));
|
||||||
|
@ -2,11 +2,7 @@ package com.annimon.hotarufx.bundles;
|
|||||||
|
|
||||||
import com.annimon.hotarufx.lib.Context;
|
import com.annimon.hotarufx.lib.Context;
|
||||||
import com.annimon.hotarufx.lib.Function;
|
import com.annimon.hotarufx.lib.Function;
|
||||||
import lombok.AccessLevel;
|
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
|
|
||||||
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
|
|
||||||
public class FunctionInfo {
|
public class FunctionInfo {
|
||||||
|
|
||||||
public static FunctionInfo of(FunctionType type, Function function) {
|
public static FunctionInfo of(FunctionType type, Function function) {
|
||||||
@ -17,10 +13,18 @@ public class FunctionInfo {
|
|||||||
return new FunctionInfo(type, extractor);
|
return new FunctionInfo(type, extractor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Getter
|
|
||||||
private final FunctionType type;
|
private final FunctionType type;
|
||||||
private final java.util.function.Function<Context, Function> functionExtractor;
|
private final java.util.function.Function<Context, Function> functionExtractor;
|
||||||
|
|
||||||
|
private FunctionInfo(FunctionType type, java.util.function.Function<Context, Function> functionExtractor) {
|
||||||
|
this.type = type;
|
||||||
|
this.functionExtractor = functionExtractor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FunctionType getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
public Function extract(Context context) {
|
public Function extract(Context context) {
|
||||||
return functionExtractor.apply(context);
|
return functionExtractor.apply(context);
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,6 @@ import java.util.HashMap;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import javafx.scene.shape.Shape;
|
import javafx.scene.shape.Shape;
|
||||||
import lombok.val;
|
|
||||||
import static com.annimon.hotarufx.bundles.FunctionInfo.of;
|
import static com.annimon.hotarufx.bundles.FunctionInfo.of;
|
||||||
import static com.annimon.hotarufx.bundles.FunctionType.COMMON;
|
import static com.annimon.hotarufx.bundles.FunctionType.COMMON;
|
||||||
|
|
||||||
@ -32,15 +31,15 @@ public class NodeUtilsBundle implements Bundle {
|
|||||||
|
|
||||||
private static Function strokePattern(Context context) {
|
private static Function strokePattern(Context context) {
|
||||||
return args -> {
|
return args -> {
|
||||||
val validator = Validator.with(args);
|
final var validator = Validator.with(args);
|
||||||
validator.checkOrOr(1, 2);
|
validator.checkOrOr(1, 2);
|
||||||
if (args[0].type() != Types.NODE || !(args[0].raw() instanceof ShapeNode)) {
|
if (args[0].type() != Types.NODE || !(args[0].raw() instanceof ShapeNode)) {
|
||||||
throw new TypeException("Shape required at first argument");
|
throw new TypeException("Shape required at first argument");
|
||||||
}
|
}
|
||||||
val shape = (Shape) ((ShapeNode) args[0].raw()).getFxNode();
|
final var shape = (Shape) ((ShapeNode) args[0].raw()).getFxNode();
|
||||||
if (args.length == 2) {
|
if (args.length == 2) {
|
||||||
val array = validator.requireArrayAt(1);
|
final var array = validator.requireArrayAt(1);
|
||||||
val dashList = array.stream()
|
final var dashList = array.stream()
|
||||||
.map(Value::asDouble)
|
.map(Value::asDouble)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
shape.getStrokeDashArray().setAll(dashList);
|
shape.getStrokeDashArray().setAll(dashList);
|
||||||
|
@ -12,7 +12,6 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import lombok.val;
|
|
||||||
import static com.annimon.hotarufx.bundles.FunctionInfo.of;
|
import static com.annimon.hotarufx.bundles.FunctionInfo.of;
|
||||||
import static com.annimon.hotarufx.bundles.FunctionType.NODE;
|
import static com.annimon.hotarufx.bundles.FunctionType.NODE;
|
||||||
|
|
||||||
@ -41,8 +40,8 @@ public class NodesBundle implements Bundle {
|
|||||||
|
|
||||||
private static Function node(Supplier<? extends ObjectNode> supplier) {
|
private static Function node(Supplier<? extends ObjectNode> supplier) {
|
||||||
return args -> {
|
return args -> {
|
||||||
val map = Validator.with(args).requireMapAt(0);
|
final var map = Validator.with(args).requireMapAt(0);
|
||||||
val node = new NodeValue(supplier.get());
|
final var node = new NodeValue(supplier.get());
|
||||||
node.fill(map);
|
node.fill(map);
|
||||||
return node;
|
return node;
|
||||||
};
|
};
|
||||||
@ -50,12 +49,12 @@ public class NodesBundle implements Bundle {
|
|||||||
|
|
||||||
private static Function poly(java.util.function.Function<List<Double>, ObjectNode> ctor) {
|
private static Function poly(java.util.function.Function<List<Double>, ObjectNode> ctor) {
|
||||||
return args -> {
|
return args -> {
|
||||||
val validator = Validator.with(args);
|
final var validator = Validator.with(args);
|
||||||
val map = validator.requireMapAt(1);
|
final var map = validator.requireMapAt(1);
|
||||||
val points = validator.requireArrayAt(0).stream()
|
final var points = validator.requireArrayAt(0).stream()
|
||||||
.map(Value::asDouble)
|
.map(Value::asDouble)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
val node = new NodeValue(ctor.apply(points));
|
final var node = new NodeValue(ctor.apply(points));
|
||||||
node.fill(map);
|
node.fill(map);
|
||||||
return node;
|
return node;
|
||||||
};
|
};
|
||||||
@ -63,10 +62,10 @@ public class NodesBundle implements Bundle {
|
|||||||
|
|
||||||
private static Function image() {
|
private static Function image() {
|
||||||
return args -> {
|
return args -> {
|
||||||
val validator = Validator.with(args);
|
final var validator = Validator.with(args);
|
||||||
val map = validator.requireMapAt(1);
|
final var map = validator.requireMapAt(1);
|
||||||
val url = args[0].asString();
|
final var url = args[0].asString();
|
||||||
val node = new NodeValue(new ImageNode(url));
|
final var node = new NodeValue(new ImageNode(url));
|
||||||
node.fill(map);
|
node.fill(map);
|
||||||
return node;
|
return node;
|
||||||
};
|
};
|
||||||
@ -74,7 +73,7 @@ public class NodesBundle implements Bundle {
|
|||||||
|
|
||||||
private static Function group() {
|
private static Function group() {
|
||||||
return args -> {
|
return args -> {
|
||||||
val nodes = Arrays.stream(args)
|
final var nodes = Arrays.stream(args)
|
||||||
.filter(v -> v.type() == Types.NODE)
|
.filter(v -> v.type() == Types.NODE)
|
||||||
.map(v -> ((NodeValue) v).getNode())
|
.map(v -> ((NodeValue) v).getNode())
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
@ -2,7 +2,6 @@ package com.annimon.hotarufx.exceptions;
|
|||||||
|
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
import lombok.val;
|
|
||||||
|
|
||||||
public final class Exceptions {
|
public final class Exceptions {
|
||||||
|
|
||||||
@ -11,7 +10,7 @@ public final class Exceptions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static String stackTraceToString(Throwable throwable) {
|
public static String stackTraceToString(Throwable throwable) {
|
||||||
val sw = new StringWriter();
|
final var sw = new StringWriter();
|
||||||
throwable.printStackTrace(new PrintWriter(sw));
|
throwable.printStackTrace(new PrintWriter(sw));
|
||||||
return sw.toString();
|
return sw.toString();
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,6 @@ import java.util.function.Consumer;
|
|||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
import javafx.stage.FileChooser;
|
import javafx.stage.FileChooser;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
import lombok.val;
|
|
||||||
|
|
||||||
public class FileManager implements DocumentManager {
|
public class FileManager implements DocumentManager {
|
||||||
|
|
||||||
@ -55,7 +54,7 @@ public class FileManager implements DocumentManager {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
val content = readFile(currentFile);
|
final var content = readFile(currentFile);
|
||||||
if (content.isEmpty()) {
|
if (content.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -80,7 +79,7 @@ public class FileManager implements DocumentManager {
|
|||||||
} else {
|
} else {
|
||||||
fileChooser.setInitialFileName("animation.hfx");
|
fileChooser.setInitialFileName("animation.hfx");
|
||||||
}
|
}
|
||||||
val newFile = fileChooser.showSaveDialog(stage);
|
final var newFile = fileChooser.showSaveDialog(stage);
|
||||||
if (newFile == null) {
|
if (newFile == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -3,18 +3,18 @@ package com.annimon.hotarufx.io;
|
|||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import lombok.val;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
public class IOStream {
|
public class IOStream {
|
||||||
|
|
||||||
public static String readContent(InputStream is) throws IOException {
|
public static String readContent(InputStream is) throws IOException {
|
||||||
val baos = new ByteArrayOutputStream();
|
final var baos = new ByteArrayOutputStream();
|
||||||
val bufferSize = 4096;
|
final var bufferSize = 4096;
|
||||||
val buffer = new byte[bufferSize];
|
final var buffer = new byte[bufferSize];
|
||||||
int read;
|
int read;
|
||||||
while ((read = is.read(buffer, 0, bufferSize)) != -1) {
|
while ((read = is.read(buffer, 0, bufferSize)) != -1) {
|
||||||
baos.write(buffer, 0, read);
|
baos.write(buffer, 0, read);
|
||||||
}
|
}
|
||||||
return baos.toString("UTF-8");
|
return baos.toString(StandardCharsets.UTF_8);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,12 +3,11 @@ package com.annimon.hotarufx.lexer;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import lombok.val;
|
|
||||||
|
|
||||||
public class HotaruLexer extends Lexer {
|
public class HotaruLexer extends Lexer {
|
||||||
|
|
||||||
public static List<Token> tokenize(String input) {
|
public static List<Token> tokenize(String input) {
|
||||||
val lexer = new HotaruLexer(input);
|
final var lexer = new HotaruLexer(input);
|
||||||
lexer.tokenize();
|
lexer.tokenize();
|
||||||
return lexer.getTokens();
|
return lexer.getTokens();
|
||||||
}
|
}
|
||||||
@ -48,7 +47,7 @@ public class HotaruLexer extends Lexer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Token nextToken() {
|
public Token nextToken() {
|
||||||
val current = peek(0);
|
final var current = peek(0);
|
||||||
if (Character.isDigit(current)) return tokenizeNumber();
|
if (Character.isDigit(current)) return tokenizeNumber();
|
||||||
else if (Character.isJavaIdentifierStart(current)) return tokenizeWord();
|
else if (Character.isJavaIdentifierStart(current)) return tokenizeWord();
|
||||||
else if (current == '#') return tokenizeComment();
|
else if (current == '#') return tokenizeComment();
|
||||||
@ -99,7 +98,7 @@ public class HotaruLexer extends Lexer {
|
|||||||
current = next();
|
current = next();
|
||||||
}
|
}
|
||||||
|
|
||||||
val word = getBuffer().toString();
|
final var word = getBuffer().toString();
|
||||||
return addToken(KEYWORDS.getOrDefault(word, HotaruTokenId.WORD));
|
return addToken(KEYWORDS.getOrDefault(word, HotaruTokenId.WORD));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,7 +109,7 @@ public class HotaruLexer extends Lexer {
|
|||||||
char current = peek(0);
|
char current = peek(0);
|
||||||
while (true) {
|
while (true) {
|
||||||
if (current == '\\') {
|
if (current == '\\') {
|
||||||
val buffer = getBuffer();
|
final var buffer = getBuffer();
|
||||||
current = next();
|
current = next();
|
||||||
if (current == openChar) {
|
if (current == openChar) {
|
||||||
current = next();
|
current = next();
|
||||||
@ -166,7 +165,7 @@ public class HotaruLexer extends Lexer {
|
|||||||
char current = peek(0);
|
char current = peek(0);
|
||||||
clearBuffer();
|
clearBuffer();
|
||||||
while (true) {
|
while (true) {
|
||||||
val text = getBuffer().toString();
|
final var text = getBuffer().toString();
|
||||||
if (!text.isEmpty() && !OPERATORS.containsKey(text + current)) {
|
if (!text.isEmpty() && !OPERATORS.containsKey(text + current)) {
|
||||||
return addToken(OPERATORS.get(text), "", text.length());
|
return addToken(OPERATORS.get(text), "", text.length());
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,5 @@
|
|||||||
package com.annimon.hotarufx.lexer;
|
package com.annimon.hotarufx.lexer;
|
||||||
|
|
||||||
import lombok.AccessLevel;
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
|
|
||||||
@AllArgsConstructor(access = AccessLevel.PACKAGE)
|
|
||||||
public enum HotaruTokenId {
|
public enum HotaruTokenId {
|
||||||
|
|
||||||
NUMBER(Category.NUMBER),
|
NUMBER(Category.NUMBER),
|
||||||
@ -41,6 +37,10 @@ public enum HotaruTokenId {
|
|||||||
|
|
||||||
private final Category category;
|
private final Category category;
|
||||||
|
|
||||||
|
HotaruTokenId(Category category) {
|
||||||
|
this.category = category;
|
||||||
|
}
|
||||||
|
|
||||||
public String getPrimaryCategory() {
|
public String getPrimaryCategory() {
|
||||||
return category.name().toLowerCase();
|
return category.name().toLowerCase();
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,8 @@ package com.annimon.hotarufx.lexer;
|
|||||||
import com.annimon.hotarufx.exceptions.LexerException;
|
import com.annimon.hotarufx.exceptions.LexerException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import lombok.val;
|
|
||||||
|
|
||||||
|
@SuppressWarnings("WeakerAccess")
|
||||||
public abstract class Lexer {
|
public abstract class Lexer {
|
||||||
|
|
||||||
private final String input;
|
private final String input;
|
||||||
@ -88,7 +88,7 @@ public abstract class Lexer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected Token addToken(HotaruTokenId tokenId, String text, int length) {
|
protected Token addToken(HotaruTokenId tokenId, String text, int length) {
|
||||||
val token = createToken(tokenId, text, length);
|
final var token = createToken(tokenId, text, length);
|
||||||
tokens.add(token);
|
tokens.add(token);
|
||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,46 @@
|
|||||||
package com.annimon.hotarufx.lexer;
|
package com.annimon.hotarufx.lexer;
|
||||||
|
|
||||||
import lombok.Data;
|
import java.util.Objects;
|
||||||
|
|
||||||
@Data
|
|
||||||
public class SourcePosition {
|
public class SourcePosition {
|
||||||
|
|
||||||
private final int position;
|
private final int position;
|
||||||
private final int row;
|
private final int row;
|
||||||
private final int column;
|
private final int column;
|
||||||
|
|
||||||
|
public SourcePosition(int position, int row, int column) {
|
||||||
|
this.position = position;
|
||||||
|
this.row = row;
|
||||||
|
this.column = column;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPosition() {
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getRow() {
|
||||||
|
return row;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getColumn() {
|
||||||
|
return column;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
SourcePosition that = (SourcePosition) o;
|
||||||
|
return position == that.position &&
|
||||||
|
row == that.row &&
|
||||||
|
column == that.column;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(position, row, column);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "[" + row + ", " + column + "]";
|
return "[" + row + ", " + column + "]";
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
package com.annimon.hotarufx.lexer;
|
package com.annimon.hotarufx.lexer;
|
||||||
|
|
||||||
import lombok.Data;
|
import java.util.Objects;
|
||||||
|
|
||||||
@Data
|
|
||||||
public class Token {
|
public class Token {
|
||||||
|
|
||||||
private final HotaruTokenId type;
|
private final HotaruTokenId type;
|
||||||
@ -10,6 +9,45 @@ public class Token {
|
|||||||
private final int length;
|
private final int length;
|
||||||
private final SourcePosition position;
|
private final SourcePosition position;
|
||||||
|
|
||||||
|
public Token(HotaruTokenId type, String text, int length, SourcePosition position) {
|
||||||
|
this.type = type;
|
||||||
|
this.text = text;
|
||||||
|
this.length = length;
|
||||||
|
this.position = position;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HotaruTokenId getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getText() {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getLength() {
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SourcePosition getPosition() {
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
Token token = (Token) o;
|
||||||
|
return length == token.length &&
|
||||||
|
type == token.type &&
|
||||||
|
Objects.equals(text, token.text) &&
|
||||||
|
Objects.equals(position, token.position);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(type, text, length, position);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return type.name() + " " + position + " " + text;
|
return type.name() + " " + position + " " + text;
|
||||||
|
@ -6,7 +6,6 @@ import java.util.Collection;
|
|||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
import lombok.Getter;
|
|
||||||
|
|
||||||
public class ArrayValue implements Value, Iterable<Value> {
|
public class ArrayValue implements Value, Iterable<Value> {
|
||||||
|
|
||||||
@ -17,7 +16,6 @@ public class ArrayValue implements Value, Iterable<Value> {
|
|||||||
.toArray(Value[]::new));
|
.toArray(Value[]::new));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Getter
|
|
||||||
private final Value[] elements;
|
private final Value[] elements;
|
||||||
|
|
||||||
public ArrayValue(int size) {
|
public ArrayValue(int size) {
|
||||||
@ -33,6 +31,10 @@ public class ArrayValue implements Value, Iterable<Value> {
|
|||||||
this(array.elements);
|
this(array.elements);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Value[] getElements() {
|
||||||
|
return elements;
|
||||||
|
}
|
||||||
|
|
||||||
public Value[] getCopyElements() {
|
public Value[] getCopyElements() {
|
||||||
final Value[] result = new Value[elements.length];
|
final Value[] result = new Value[elements.length];
|
||||||
System.arraycopy(elements, 0, result, 0, elements.length);
|
System.arraycopy(elements, 0, result, 0, elements.length);
|
||||||
|
@ -4,17 +4,16 @@ import com.annimon.hotarufx.exceptions.TypeException;
|
|||||||
import javafx.scene.text.Font;
|
import javafx.scene.text.Font;
|
||||||
import javafx.scene.text.FontPosture;
|
import javafx.scene.text.FontPosture;
|
||||||
import javafx.scene.text.FontWeight;
|
import javafx.scene.text.FontWeight;
|
||||||
import lombok.val;
|
|
||||||
|
|
||||||
public class FontValue extends MapValue {
|
public class FontValue extends MapValue {
|
||||||
|
|
||||||
public static Font toFont(MapValue mapValue) {
|
public static Font toFont(MapValue mapValue) {
|
||||||
val map = mapValue.getMap();
|
final var map = mapValue.getMap();
|
||||||
val family = map.getOrDefault("family", new StringValue(Font.getDefault().getFamily())).asString();
|
final var family = map.getOrDefault("family", new StringValue(Font.getDefault().getFamily())).asString();
|
||||||
val weight = map.getOrDefault("weight", NumberValue.of(FontWeight.NORMAL.getWeight())).asInt();
|
final var weight = map.getOrDefault("weight", NumberValue.of(FontWeight.NORMAL.getWeight())).asInt();
|
||||||
val isItalic = map.getOrDefault("italic", NumberValue.ZERO).asBoolean();
|
final var isItalic = map.getOrDefault("italic", NumberValue.ZERO).asBoolean();
|
||||||
val posture = isItalic ? FontPosture.ITALIC : FontPosture.REGULAR;
|
final var posture = isItalic ? FontPosture.ITALIC : FontPosture.REGULAR;
|
||||||
val size = map.getOrDefault("size", NumberValue.MINUS_ONE).asDouble();
|
final var size = map.getOrDefault("size", NumberValue.MINUS_ONE).asDouble();
|
||||||
return Font.font(family, FontWeight.findByWeight(weight), posture, size);
|
return Font.font(family, FontWeight.findByWeight(weight), posture, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -27,10 +26,10 @@ public class FontValue extends MapValue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void init() {
|
private void init() {
|
||||||
val map = super.getMap();
|
final var map = super.getMap();
|
||||||
map.put("family", new StringValue(font.getFamily()));
|
map.put("family", new StringValue(font.getFamily()));
|
||||||
map.put("isItalic", NumberValue.fromBoolean(font.getStyle().toLowerCase().contains("italic")));
|
map.put("isItalic", NumberValue.fromBoolean(font.getStyle().toLowerCase().contains("italic")));
|
||||||
val weight = FontWeight.findByName(font.getStyle());
|
final var weight = FontWeight.findByName(font.getStyle());
|
||||||
map.put("weight", NumberValue.of(weight != null
|
map.put("weight", NumberValue.of(weight != null
|
||||||
? (weight.getWeight())
|
? (weight.getWeight())
|
||||||
: FontWeight.NORMAL.getWeight()));
|
: FontWeight.NORMAL.getWeight()));
|
||||||
|
@ -2,19 +2,21 @@ package com.annimon.hotarufx.lib;
|
|||||||
|
|
||||||
import com.annimon.hotarufx.exceptions.TypeException;
|
import com.annimon.hotarufx.exceptions.TypeException;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import lombok.Getter;
|
|
||||||
|
|
||||||
public class FunctionValue implements Value {
|
public class FunctionValue implements Value {
|
||||||
|
|
||||||
public static final FunctionValue EMPTY = new FunctionValue(args -> NumberValue.ZERO);
|
public static final FunctionValue EMPTY = new FunctionValue(args -> NumberValue.ZERO);
|
||||||
|
|
||||||
@Getter
|
|
||||||
private final Function value;
|
private final Function value;
|
||||||
|
|
||||||
public FunctionValue(Function value) {
|
public FunctionValue(Function value) {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Function getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int type() {
|
public int type() {
|
||||||
return Types.FUNCTION;
|
return Types.FUNCTION;
|
||||||
|
@ -2,17 +2,19 @@ package com.annimon.hotarufx.lib;
|
|||||||
|
|
||||||
import com.annimon.hotarufx.exceptions.TypeException;
|
import com.annimon.hotarufx.exceptions.TypeException;
|
||||||
import javafx.animation.Interpolator;
|
import javafx.animation.Interpolator;
|
||||||
import lombok.Getter;
|
|
||||||
|
|
||||||
public class InterpolatorValue implements Value {
|
public class InterpolatorValue implements Value {
|
||||||
|
|
||||||
@Getter
|
|
||||||
private final Interpolator interpolator;
|
private final Interpolator interpolator;
|
||||||
|
|
||||||
public InterpolatorValue(Interpolator interpolator) {
|
public InterpolatorValue(Interpolator interpolator) {
|
||||||
this.interpolator = interpolator;
|
this.interpolator = interpolator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Interpolator getInterpolator() {
|
||||||
|
return interpolator;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int type() {
|
public int type() {
|
||||||
return Types.INTERPOLATOR;
|
return Types.INTERPOLATOR;
|
||||||
|
@ -9,7 +9,6 @@ import com.annimon.hotarufx.visual.objects.ObjectNode;
|
|||||||
import javafx.scene.Node;
|
import javafx.scene.Node;
|
||||||
import javafx.scene.paint.Paint;
|
import javafx.scene.paint.Paint;
|
||||||
import javafx.scene.text.Font;
|
import javafx.scene.text.Font;
|
||||||
import lombok.val;
|
|
||||||
|
|
||||||
public class NodeValue implements Value {
|
public class NodeValue implements Value {
|
||||||
|
|
||||||
@ -45,8 +44,8 @@ public class NodeValue implements Value {
|
|||||||
throw new HotaruRuntimeException("Unable to get property " + key + " from node value");
|
throw new HotaruRuntimeException("Unable to get property " + key + " from node value");
|
||||||
}
|
}
|
||||||
final Property property = bindings.get(key);
|
final Property property = bindings.get(key);
|
||||||
val timeline = property.getProperty().get();
|
final var timeline = property.getProperty().get();
|
||||||
val type = property.getType();
|
final var type = property.getType();
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case BOOLEAN:
|
case BOOLEAN:
|
||||||
return type.<Boolean>getToHFX().apply(
|
return type.<Boolean>getToHFX().apply(
|
||||||
@ -76,8 +75,8 @@ public class NodeValue implements Value {
|
|||||||
public void set(String key, Value value) {
|
public void set(String key, Value value) {
|
||||||
if (!bindings.containsKey(key)) return;
|
if (!bindings.containsKey(key)) return;
|
||||||
final Property property = bindings.get(key);
|
final Property property = bindings.get(key);
|
||||||
val timeline = property.getProperty().get();
|
final var timeline = property.getProperty().get();
|
||||||
val type = property.getType();
|
final var type = property.getType();
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case BOOLEAN:
|
case BOOLEAN:
|
||||||
((PropertyTimeline<Boolean>) timeline).getProperty().setValue(
|
((PropertyTimeline<Boolean>) timeline).getProperty().setValue(
|
||||||
@ -108,7 +107,7 @@ public class NodeValue implements Value {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Value getProperty(String key) {
|
public Value getProperty(String key) {
|
||||||
val property = bindings.get(key);
|
final var property = bindings.get(key);
|
||||||
if (property == null) {
|
if (property == null) {
|
||||||
throw new HotaruRuntimeException("Unable to get property " + key + " from node value");
|
throw new HotaruRuntimeException("Unable to get property " + key + " from node value");
|
||||||
}
|
}
|
||||||
|
@ -11,12 +11,9 @@ import javafx.animation.Interpolator;
|
|||||||
import javafx.scene.Node;
|
import javafx.scene.Node;
|
||||||
import javafx.scene.paint.Paint;
|
import javafx.scene.paint.Paint;
|
||||||
import javafx.scene.text.Font;
|
import javafx.scene.text.Font;
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.val;
|
|
||||||
|
|
||||||
public class PropertyValue implements Value {
|
public class PropertyValue implements Value {
|
||||||
|
|
||||||
@Getter
|
|
||||||
private final Property property;
|
private final Property property;
|
||||||
private final Map<String, Value> fields;
|
private final Map<String, Value> fields;
|
||||||
|
|
||||||
@ -27,13 +24,17 @@ public class PropertyValue implements Value {
|
|||||||
fields.put("clear", new FunctionValue(clear()));
|
fields.put("clear", new FunctionValue(clear()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Property getProperty() {
|
||||||
|
return property;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int type() {
|
public int type() {
|
||||||
return Types.PROPERTY;
|
return Types.PROPERTY;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Value getField(String name) {
|
public Value getField(String name) {
|
||||||
val field = fields.get(name);
|
final var field = fields.get(name);
|
||||||
if (field == null) {
|
if (field == null) {
|
||||||
throw new HotaruRuntimeException("PropertyValue does not have " + name + " field");
|
throw new HotaruRuntimeException("PropertyValue does not have " + name + " field");
|
||||||
}
|
}
|
||||||
@ -53,7 +54,7 @@ public class PropertyValue implements Value {
|
|||||||
}
|
}
|
||||||
interpolator = ((InterpolatorValue) args[2]).getInterpolator();
|
interpolator = ((InterpolatorValue) args[2]).getInterpolator();
|
||||||
}
|
}
|
||||||
val type = property.getType();
|
final var type = property.getType();
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case BOOLEAN:
|
case BOOLEAN:
|
||||||
((PropertyTimeline<Boolean>)property.getProperty().get()).add(
|
((PropertyTimeline<Boolean>)property.getProperty().get()).add(
|
||||||
|
@ -2,14 +2,19 @@ package com.annimon.hotarufx.lib;
|
|||||||
|
|
||||||
import com.annimon.hotarufx.exceptions.ArgumentsMismatchException;
|
import com.annimon.hotarufx.exceptions.ArgumentsMismatchException;
|
||||||
import com.annimon.hotarufx.exceptions.TypeException;
|
import com.annimon.hotarufx.exceptions.TypeException;
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import lombok.val;
|
|
||||||
|
|
||||||
@RequiredArgsConstructor(staticName = "with")
|
|
||||||
public class Validator {
|
public class Validator {
|
||||||
|
|
||||||
|
public static Validator with(Value[] args) {
|
||||||
|
return new Validator(args);
|
||||||
|
}
|
||||||
|
|
||||||
private final Value[] args;
|
private final Value[] args;
|
||||||
|
|
||||||
|
private Validator(Value[] args) {
|
||||||
|
this.args = args;
|
||||||
|
}
|
||||||
|
|
||||||
public Validator check(int expected) {
|
public Validator check(int expected) {
|
||||||
if (args.length != expected) throw new ArgumentsMismatchException(String.format(
|
if (args.length != expected) throw new ArgumentsMismatchException(String.format(
|
||||||
"%d %s expected, got %d", expected, pluralize(expected), args.length));
|
"%d %s expected, got %d", expected, pluralize(expected), args.length));
|
||||||
@ -38,7 +43,7 @@ public class Validator {
|
|||||||
|
|
||||||
public ArrayValue requireArrayAt(int index) {
|
public ArrayValue requireArrayAt(int index) {
|
||||||
checkAtLeast(index + 1);
|
checkAtLeast(index + 1);
|
||||||
val value = args[index];
|
final var value = args[index];
|
||||||
if (value.type() != Types.ARRAY) {
|
if (value.type() != Types.ARRAY) {
|
||||||
throw new TypeException(String.format("Array required at %d argument", index));
|
throw new TypeException(String.format("Array required at %d argument", index));
|
||||||
}
|
}
|
||||||
@ -47,7 +52,7 @@ public class Validator {
|
|||||||
|
|
||||||
public MapValue requireMapAt(int index) {
|
public MapValue requireMapAt(int index) {
|
||||||
checkAtLeast(index + 1);
|
checkAtLeast(index + 1);
|
||||||
val value = args[index];
|
final var value = args[index];
|
||||||
if (value.type() != Types.MAP) {
|
if (value.type() != Types.MAP) {
|
||||||
throw new TypeException(String.format("Map required at %d argument", index));
|
throw new TypeException(String.format("Map required at %d argument", index));
|
||||||
}
|
}
|
||||||
|
@ -9,13 +9,12 @@ import java.util.ArrayList;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import lombok.val;
|
|
||||||
|
|
||||||
public class HotaruParser extends Parser {
|
public class HotaruParser extends Parser {
|
||||||
|
|
||||||
public static Node parse(List<Token> tokens) {
|
public static Node parse(List<Token> tokens) {
|
||||||
val parser = new HotaruParser(tokens);
|
final var parser = new HotaruParser(tokens);
|
||||||
val program = parser.parse();
|
final var program = parser.parse();
|
||||||
if (parser.getParseErrors().hasErrors()) {
|
if (parser.getParseErrors().hasErrors()) {
|
||||||
throw new ParseException(parser.getParseErrors().toString());
|
throw new ParseException(parser.getParseErrors().toString());
|
||||||
}
|
}
|
||||||
@ -27,7 +26,7 @@ public class HotaruParser extends Parser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Node block() {
|
private Node block() {
|
||||||
val block = new BlockNode();
|
final var block = new BlockNode();
|
||||||
block.start(getSourcePosition());
|
block.start(getSourcePosition());
|
||||||
consume(HotaruTokenId.LBRACE);
|
consume(HotaruTokenId.LBRACE);
|
||||||
while (!match(HotaruTokenId.RBRACE)) {
|
while (!match(HotaruTokenId.RBRACE)) {
|
||||||
@ -55,7 +54,7 @@ public class HotaruParser extends Parser {
|
|||||||
|
|
||||||
private Node functionChain(Node qualifiedNameExpr) {
|
private Node functionChain(Node qualifiedNameExpr) {
|
||||||
// f1()()() || f1().f2().f3() || f1().key
|
// f1()()() || f1().f2().f3() || f1().key
|
||||||
val expr = function(qualifiedNameExpr);
|
final var expr = function(qualifiedNameExpr);
|
||||||
if (lookMatch(0, HotaruTokenId.LPAREN)) {
|
if (lookMatch(0, HotaruTokenId.LPAREN)) {
|
||||||
return functionChain(expr);
|
return functionChain(expr);
|
||||||
}
|
}
|
||||||
@ -64,7 +63,7 @@ public class HotaruParser extends Parser {
|
|||||||
|
|
||||||
private Node objectAccess(Node expr) {
|
private Node objectAccess(Node expr) {
|
||||||
if (lookMatch(0, HotaruTokenId.DOT)) {
|
if (lookMatch(0, HotaruTokenId.DOT)) {
|
||||||
val indices = variableSuffix();
|
final var indices = variableSuffix();
|
||||||
if (indices == null || indices.isEmpty())
|
if (indices == null || indices.isEmpty())
|
||||||
return expr;
|
return expr;
|
||||||
|
|
||||||
@ -81,7 +80,7 @@ public class HotaruParser extends Parser {
|
|||||||
private FunctionNode function(Node qualifiedNameExpr) {
|
private FunctionNode function(Node qualifiedNameExpr) {
|
||||||
// function(arg1, arg2, ...)
|
// function(arg1, arg2, ...)
|
||||||
consume(HotaruTokenId.LPAREN);
|
consume(HotaruTokenId.LPAREN);
|
||||||
val function = new FunctionNode(qualifiedNameExpr);
|
final var function = new FunctionNode(qualifiedNameExpr);
|
||||||
while (!match(HotaruTokenId.RPAREN)) {
|
while (!match(HotaruTokenId.RPAREN)) {
|
||||||
function.addArgument(expression());
|
function.addArgument(expression());
|
||||||
match(HotaruTokenId.COMMA);
|
match(HotaruTokenId.COMMA);
|
||||||
@ -92,7 +91,7 @@ public class HotaruParser extends Parser {
|
|||||||
private Node array() {
|
private Node array() {
|
||||||
// [value1, value2, ...]
|
// [value1, value2, ...]
|
||||||
consume(HotaruTokenId.LBRACKET);
|
consume(HotaruTokenId.LBRACKET);
|
||||||
val elements = new ArrayList<Node>();
|
final var elements = new ArrayList<Node>();
|
||||||
while (!match(HotaruTokenId.RBRACKET)) {
|
while (!match(HotaruTokenId.RBRACKET)) {
|
||||||
elements.add(expression());
|
elements.add(expression());
|
||||||
match(HotaruTokenId.COMMA);
|
match(HotaruTokenId.COMMA);
|
||||||
@ -105,9 +104,9 @@ public class HotaruParser extends Parser {
|
|||||||
consume(HotaruTokenId.LBRACE);
|
consume(HotaruTokenId.LBRACE);
|
||||||
final Map<String, Node> elements = new HashMap<>();
|
final Map<String, Node> elements = new HashMap<>();
|
||||||
while (!match(HotaruTokenId.RBRACE)) {
|
while (!match(HotaruTokenId.RBRACE)) {
|
||||||
val key = consume(HotaruTokenId.WORD).getText();
|
final var key = consume(HotaruTokenId.WORD).getText();
|
||||||
consume(HotaruTokenId.COLON);
|
consume(HotaruTokenId.COLON);
|
||||||
val value = expression();
|
final var value = expression();
|
||||||
elements.put(key, value);
|
elements.put(key, value);
|
||||||
match(HotaruTokenId.COMMA);
|
match(HotaruTokenId.COMMA);
|
||||||
}
|
}
|
||||||
@ -120,7 +119,7 @@ public class HotaruParser extends Parser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Node assignment() {
|
private Node assignment() {
|
||||||
val assignment = assignmentStrict();
|
final var assignment = assignmentStrict();
|
||||||
if (assignment != null) {
|
if (assignment != null) {
|
||||||
return assignment;
|
return assignment;
|
||||||
}
|
}
|
||||||
@ -129,8 +128,8 @@ public class HotaruParser extends Parser {
|
|||||||
|
|
||||||
private Node assignmentStrict() {
|
private Node assignmentStrict() {
|
||||||
final int position = pos;
|
final int position = pos;
|
||||||
val startSourcePosition = getSourcePosition();
|
final var startSourcePosition = getSourcePosition();
|
||||||
val targetExpr = qualifiedName();
|
final var targetExpr = qualifiedName();
|
||||||
if ((targetExpr == null) || !(targetExpr instanceof Accessible)) {
|
if ((targetExpr == null) || !(targetExpr instanceof Accessible)) {
|
||||||
pos = position;
|
pos = position;
|
||||||
return null;
|
return null;
|
||||||
@ -157,7 +156,7 @@ public class HotaruParser extends Parser {
|
|||||||
|
|
||||||
private Node primary() {
|
private Node primary() {
|
||||||
if (match(HotaruTokenId.LPAREN)) {
|
if (match(HotaruTokenId.LPAREN)) {
|
||||||
val result = expression();
|
final var result = expression();
|
||||||
match(HotaruTokenId.RPAREN);
|
match(HotaruTokenId.RPAREN);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -189,8 +188,8 @@ public class HotaruParser extends Parser {
|
|||||||
}
|
}
|
||||||
// node@prop || map.node@prop
|
// node@prop || map.node@prop
|
||||||
if (match(HotaruTokenId.AT)) {
|
if (match(HotaruTokenId.AT)) {
|
||||||
val propName = consume(HotaruTokenId.WORD).getText();
|
final var propName = consume(HotaruTokenId.WORD).getText();
|
||||||
val expr = new PropertyNode(qualifiedNameExpr, propName);
|
final var expr = new PropertyNode(qualifiedNameExpr, propName);
|
||||||
return objectAccess(expr);
|
return objectAccess(expr);
|
||||||
}
|
}
|
||||||
return qualifiedNameExpr;
|
return qualifiedNameExpr;
|
||||||
@ -221,8 +220,8 @@ public class HotaruParser extends Parser {
|
|||||||
final List<Node> indices = new ArrayList<>();
|
final List<Node> indices = new ArrayList<>();
|
||||||
while (lookMatch(0, HotaruTokenId.DOT)) {
|
while (lookMatch(0, HotaruTokenId.DOT)) {
|
||||||
if (match(HotaruTokenId.DOT)) {
|
if (match(HotaruTokenId.DOT)) {
|
||||||
val fieldName = consume(HotaruTokenId.WORD).getText();
|
final var fieldName = consume(HotaruTokenId.WORD).getText();
|
||||||
val key = new ValueNode(fieldName);
|
final var key = new ValueNode(fieldName);
|
||||||
indices.add(key);
|
indices.add(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -230,7 +229,7 @@ public class HotaruParser extends Parser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Node value() {
|
private Node value() {
|
||||||
val current = get(0);
|
final var current = get(0);
|
||||||
if (match(HotaruTokenId.TRUE)) {
|
if (match(HotaruTokenId.TRUE)) {
|
||||||
return new ValueNode(NumberValue.ONE);
|
return new ValueNode(NumberValue.ONE);
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,6 @@ import com.annimon.hotarufx.lexer.Token;
|
|||||||
import com.annimon.hotarufx.parser.ast.BlockNode;
|
import com.annimon.hotarufx.parser.ast.BlockNode;
|
||||||
import com.annimon.hotarufx.parser.ast.Node;
|
import com.annimon.hotarufx.parser.ast.Node;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import lombok.val;
|
|
||||||
|
|
||||||
public abstract class Parser {
|
public abstract class Parser {
|
||||||
|
|
||||||
@ -38,7 +37,7 @@ public abstract class Parser {
|
|||||||
|
|
||||||
public Node parse() {
|
public Node parse() {
|
||||||
parseErrors.clear();
|
parseErrors.clear();
|
||||||
val result = new BlockNode();
|
final var result = new BlockNode();
|
||||||
result.start(getSourcePosition());
|
result.start(getSourcePosition());
|
||||||
while (!match(HotaruTokenId.EOF)) {
|
while (!match(HotaruTokenId.EOF)) {
|
||||||
try {
|
try {
|
||||||
@ -77,7 +76,7 @@ public abstract class Parser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected Token consume(HotaruTokenId type) {
|
protected Token consume(HotaruTokenId type) {
|
||||||
val current = get(0);
|
final var current = get(0);
|
||||||
if (type != current.getType()) {
|
if (type != current.getType()) {
|
||||||
throw new ParseException("Token " + current + " doesn't match " + type, current.getPosition());
|
throw new ParseException("Token " + current + " doesn't match " + type, current.getPosition());
|
||||||
}
|
}
|
||||||
@ -86,7 +85,7 @@ public abstract class Parser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected boolean match(HotaruTokenId type) {
|
protected boolean match(HotaruTokenId type) {
|
||||||
val current = get(0);
|
final var current = get(0);
|
||||||
if (type != current.getType()) return false;
|
if (type != current.getType()) return false;
|
||||||
pos++;
|
pos++;
|
||||||
return true;
|
return true;
|
||||||
@ -97,7 +96,7 @@ public abstract class Parser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected Token get(int relativePosition) {
|
protected Token get(int relativePosition) {
|
||||||
val position = pos + relativePosition;
|
final var position = pos + relativePosition;
|
||||||
if (position >= size) return EOF;
|
if (position >= size) return EOF;
|
||||||
return tokens.get(position);
|
return tokens.get(position);
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,30 @@
|
|||||||
package com.annimon.hotarufx.parser.ast;
|
package com.annimon.hotarufx.parser.ast;
|
||||||
|
|
||||||
import com.annimon.hotarufx.lexer.SourcePosition;
|
import com.annimon.hotarufx.lexer.SourcePosition;
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.Setter;
|
|
||||||
import lombok.experimental.Accessors;
|
|
||||||
|
|
||||||
@Accessors(fluent = true)
|
|
||||||
public abstract class ASTNode implements Node {
|
public abstract class ASTNode implements Node {
|
||||||
|
|
||||||
@Getter @Setter
|
|
||||||
private SourcePosition start;
|
private SourcePosition start;
|
||||||
@Getter @Setter
|
|
||||||
private SourcePosition end;
|
private SourcePosition end;
|
||||||
|
|
||||||
|
public SourcePosition start() {
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SourcePosition end() {
|
||||||
|
return end;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ASTNode start(SourcePosition start) {
|
||||||
|
this.start = start;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ASTNode end(SourcePosition end) {
|
||||||
|
this.end = end;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public String getSourceRange() {
|
public String getSourceRange() {
|
||||||
return start + " .. " + end;
|
return start + " .. " + end;
|
||||||
}
|
}
|
||||||
|
@ -3,20 +3,25 @@ package com.annimon.hotarufx.parser.ast;
|
|||||||
import com.annimon.hotarufx.lib.Value;
|
import com.annimon.hotarufx.lib.Value;
|
||||||
import com.annimon.hotarufx.parser.visitors.ResultVisitor;
|
import com.annimon.hotarufx.parser.visitors.ResultVisitor;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class AccessNode extends ASTNode implements Accessible {
|
public class AccessNode extends ASTNode implements Accessible {
|
||||||
|
|
||||||
@Getter
|
|
||||||
public final Node root;
|
public final Node root;
|
||||||
public final List<Node> indices;
|
public final List<Node> indices;
|
||||||
|
|
||||||
|
public AccessNode(Node root, List<Node> indices) {
|
||||||
|
this.root = root;
|
||||||
|
this.indices = indices;
|
||||||
|
}
|
||||||
|
|
||||||
public AccessNode(String variable, List<Node> indices) {
|
public AccessNode(String variable, List<Node> indices) {
|
||||||
this(new VariableNode(variable), indices);
|
this(new VariableNode(variable), indices);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Node getRoot() {
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <R, T> R accept(ResultVisitor<R, T> visitor, T input) {
|
public <R, T> R accept(ResultVisitor<R, T> visitor, T input) {
|
||||||
return visitor.visit(this, input);
|
return visitor.visit(this, input);
|
||||||
|
@ -2,13 +2,15 @@ package com.annimon.hotarufx.parser.ast;
|
|||||||
|
|
||||||
import com.annimon.hotarufx.parser.visitors.ResultVisitor;
|
import com.annimon.hotarufx.parser.visitors.ResultVisitor;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class ArrayNode extends ASTNode {
|
public class ArrayNode extends ASTNode {
|
||||||
|
|
||||||
public final List<Node> elements;
|
public final List<Node> elements;
|
||||||
|
|
||||||
|
public ArrayNode(List<Node> elements) {
|
||||||
|
this.elements = elements;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <R, T> R accept(ResultVisitor<R, T> visitor, T input) {
|
public <R, T> R accept(ResultVisitor<R, T> visitor, T input) {
|
||||||
return visitor.visit(this, input);
|
return visitor.visit(this, input);
|
||||||
|
@ -1,14 +1,17 @@
|
|||||||
package com.annimon.hotarufx.parser.ast;
|
package com.annimon.hotarufx.parser.ast;
|
||||||
|
|
||||||
import com.annimon.hotarufx.parser.visitors.ResultVisitor;
|
import com.annimon.hotarufx.parser.visitors.ResultVisitor;
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class AssignNode extends ASTNode {
|
public class AssignNode extends ASTNode {
|
||||||
|
|
||||||
public final Accessible target;
|
public final Accessible target;
|
||||||
public final Node value;
|
public final Node value;
|
||||||
|
|
||||||
|
public AssignNode(Accessible target, Node value) {
|
||||||
|
this.target = target;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <R, T> R accept(ResultVisitor<R, T> visitor, T input) {
|
public <R, T> R accept(ResultVisitor<R, T> visitor, T input) {
|
||||||
return visitor.visit(this, input);
|
return visitor.visit(this, input);
|
||||||
|
@ -2,13 +2,15 @@ package com.annimon.hotarufx.parser.ast;
|
|||||||
|
|
||||||
import com.annimon.hotarufx.parser.visitors.ResultVisitor;
|
import com.annimon.hotarufx.parser.visitors.ResultVisitor;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class MapNode extends ASTNode {
|
public class MapNode extends ASTNode {
|
||||||
|
|
||||||
public final Map<String, Node> elements;
|
public final Map<String, Node> elements;
|
||||||
|
|
||||||
|
public MapNode(Map<String, Node> elements) {
|
||||||
|
this.elements = elements;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <R, T> R accept(ResultVisitor<R, T> visitor, T input) {
|
public <R, T> R accept(ResultVisitor<R, T> visitor, T input) {
|
||||||
return visitor.visit(this, input);
|
return visitor.visit(this, input);
|
||||||
|
@ -1,14 +1,17 @@
|
|||||||
package com.annimon.hotarufx.parser.ast;
|
package com.annimon.hotarufx.parser.ast;
|
||||||
|
|
||||||
import com.annimon.hotarufx.parser.visitors.ResultVisitor;
|
import com.annimon.hotarufx.parser.visitors.ResultVisitor;
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class PropertyNode extends ASTNode {
|
public class PropertyNode extends ASTNode {
|
||||||
|
|
||||||
public final Node node;
|
public final Node node;
|
||||||
public final String property;
|
public final String property;
|
||||||
|
|
||||||
|
public PropertyNode(Node node, String property) {
|
||||||
|
this.node = node;
|
||||||
|
this.property = property;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <R, T> R accept(ResultVisitor<R, T> visitor, T input) {
|
public <R, T> R accept(ResultVisitor<R, T> visitor, T input) {
|
||||||
return visitor.visit(this, input);
|
return visitor.visit(this, input);
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
package com.annimon.hotarufx.parser.ast;
|
package com.annimon.hotarufx.parser.ast;
|
||||||
|
|
||||||
import com.annimon.hotarufx.parser.visitors.ResultVisitor;
|
import com.annimon.hotarufx.parser.visitors.ResultVisitor;
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class UnaryNode extends ASTNode {
|
public class UnaryNode extends ASTNode {
|
||||||
|
|
||||||
public enum Operator { NEGATE };
|
public enum Operator { NEGATE };
|
||||||
@ -11,6 +9,11 @@ public class UnaryNode extends ASTNode {
|
|||||||
public final Operator operator;
|
public final Operator operator;
|
||||||
public final Node node;
|
public final Node node;
|
||||||
|
|
||||||
|
public UnaryNode(Operator operator, Node node) {
|
||||||
|
this.operator = operator;
|
||||||
|
this.node = node;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <R, T> R accept(ResultVisitor<R, T> visitor, T input) {
|
public <R, T> R accept(ResultVisitor<R, T> visitor, T input) {
|
||||||
return visitor.visit(this, input);
|
return visitor.visit(this, input);
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
package com.annimon.hotarufx.parser.ast;
|
package com.annimon.hotarufx.parser.ast;
|
||||||
|
|
||||||
import com.annimon.hotarufx.parser.visitors.ResultVisitor;
|
import com.annimon.hotarufx.parser.visitors.ResultVisitor;
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class UnitNode extends ASTNode {
|
public class UnitNode extends ASTNode {
|
||||||
|
|
||||||
public enum Unit {MILLISECONDS, SECONDS};
|
public enum Unit {MILLISECONDS, SECONDS};
|
||||||
@ -11,6 +9,11 @@ public class UnitNode extends ASTNode {
|
|||||||
public final Node value;
|
public final Node value;
|
||||||
public final Unit operator;
|
public final Unit operator;
|
||||||
|
|
||||||
|
public UnitNode(Node value, Unit operator) {
|
||||||
|
this.value = value;
|
||||||
|
this.operator = operator;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <R, T> R accept(ResultVisitor<R, T> visitor, T input) {
|
public <R, T> R accept(ResultVisitor<R, T> visitor, T input) {
|
||||||
return visitor.visit(this, input);
|
return visitor.visit(this, input);
|
||||||
|
@ -4,13 +4,15 @@ import com.annimon.hotarufx.lib.NumberValue;
|
|||||||
import com.annimon.hotarufx.lib.StringValue;
|
import com.annimon.hotarufx.lib.StringValue;
|
||||||
import com.annimon.hotarufx.lib.Value;
|
import com.annimon.hotarufx.lib.Value;
|
||||||
import com.annimon.hotarufx.parser.visitors.ResultVisitor;
|
import com.annimon.hotarufx.parser.visitors.ResultVisitor;
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class ValueNode extends ASTNode {
|
public class ValueNode extends ASTNode {
|
||||||
|
|
||||||
public final Value value;
|
public final Value value;
|
||||||
|
|
||||||
|
public ValueNode(Value value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
public ValueNode(Number value) {
|
public ValueNode(Number value) {
|
||||||
this(NumberValue.of(value));
|
this(NumberValue.of(value));
|
||||||
}
|
}
|
||||||
|
@ -2,13 +2,15 @@ package com.annimon.hotarufx.parser.ast;
|
|||||||
|
|
||||||
import com.annimon.hotarufx.lib.Value;
|
import com.annimon.hotarufx.lib.Value;
|
||||||
import com.annimon.hotarufx.parser.visitors.ResultVisitor;
|
import com.annimon.hotarufx.parser.visitors.ResultVisitor;
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class VariableNode extends ASTNode implements Accessible {
|
public class VariableNode extends ASTNode implements Accessible {
|
||||||
|
|
||||||
public final String name;
|
public final String name;
|
||||||
|
|
||||||
|
public VariableNode(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <R, T> R accept(ResultVisitor<R, T> visitor, T input) {
|
public <R, T> R accept(ResultVisitor<R, T> visitor, T input) {
|
||||||
return visitor.visit(this, input);
|
return visitor.visit(this, input);
|
||||||
|
@ -10,7 +10,6 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
import lombok.val;
|
|
||||||
|
|
||||||
public class InterpreterVisitor implements ResultVisitor<Value, Context> {
|
public class InterpreterVisitor implements ResultVisitor<Value, Context> {
|
||||||
|
|
||||||
@ -21,7 +20,7 @@ public class InterpreterVisitor implements ResultVisitor<Value, Context> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Value visit(ArrayNode node, Context context) {
|
public Value visit(ArrayNode node, Context context) {
|
||||||
val elements = node.elements.stream()
|
final var elements = node.elements.stream()
|
||||||
.map(el -> el.accept(this, context))
|
.map(el -> el.accept(this, context))
|
||||||
.toArray(Value[]::new);
|
.toArray(Value[]::new);
|
||||||
return new ArrayValue(elements);
|
return new ArrayValue(elements);
|
||||||
@ -29,7 +28,7 @@ public class InterpreterVisitor implements ResultVisitor<Value, Context> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Value visit(AssignNode node, Context context) {
|
public Value visit(AssignNode node, Context context) {
|
||||||
val value = node.value.accept(this, context);
|
final var value = node.value.accept(this, context);
|
||||||
return node.target.set(this, value, context);
|
return node.target.set(this, value, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,20 +43,20 @@ public class InterpreterVisitor implements ResultVisitor<Value, Context> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Value visit(FunctionNode node, Context context) {
|
public Value visit(FunctionNode node, Context context) {
|
||||||
val value = node.functionNode.accept(this, context);
|
final var value = node.functionNode.accept(this, context);
|
||||||
final Function function;
|
final Function function;
|
||||||
switch (value.type()) {
|
switch (value.type()) {
|
||||||
case Types.FUNCTION:
|
case Types.FUNCTION:
|
||||||
function = ((FunctionValue) value).getValue();
|
function = ((FunctionValue) value).getValue();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
val functionName = value.asString();
|
final var functionName = value.asString();
|
||||||
function = context.functions().get(functionName);
|
function = context.functions().get(functionName);
|
||||||
if (function == null)
|
if (function == null)
|
||||||
throw new FunctionNotFoundException(functionName, node.start(), node.end());
|
throw new FunctionNotFoundException(functionName, node.start(), node.end());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
val args = node.arguments.stream()
|
final var args = node.arguments.stream()
|
||||||
.map(n -> n.accept(this, context))
|
.map(n -> n.accept(this, context))
|
||||||
.toArray(Value[]::new);
|
.toArray(Value[]::new);
|
||||||
return function.execute(args);
|
return function.execute(args);
|
||||||
@ -74,11 +73,11 @@ public class InterpreterVisitor implements ResultVisitor<Value, Context> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Value visit(PropertyNode node, Context context) {
|
public Value visit(PropertyNode node, Context context) {
|
||||||
val value = node.node.accept(this, context);
|
final var value = node.node.accept(this, context);
|
||||||
if (value.type() != Types.NODE) {
|
if (value.type() != Types.NODE) {
|
||||||
throw new TypeException("Node value expected");
|
throw new TypeException("Node value expected");
|
||||||
}
|
}
|
||||||
val nodeValue = (NodeValue) value;
|
final var nodeValue = (NodeValue) value;
|
||||||
return nodeValue.getProperty(node.property);
|
return nodeValue.getProperty(node.property);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,7 +85,7 @@ public class InterpreterVisitor implements ResultVisitor<Value, Context> {
|
|||||||
public Value visit(UnaryNode node, Context context) {
|
public Value visit(UnaryNode node, Context context) {
|
||||||
switch (node.operator) {
|
switch (node.operator) {
|
||||||
case NEGATE:
|
case NEGATE:
|
||||||
val value = node.node.accept(this, context);
|
final var value = node.node.accept(this, context);
|
||||||
if (value.type() == Types.STRING) {
|
if (value.type() == Types.STRING) {
|
||||||
final StringBuilder sb = new StringBuilder(value.asString());
|
final StringBuilder sb = new StringBuilder(value.asString());
|
||||||
return new StringValue(sb.reverse().toString());
|
return new StringValue(sb.reverse().toString());
|
||||||
@ -112,8 +111,8 @@ public class InterpreterVisitor implements ResultVisitor<Value, Context> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Value visit(UnitNode node, Context context) {
|
public Value visit(UnitNode node, Context context) {
|
||||||
val value = node.value.accept(this, context);
|
final var value = node.value.accept(this, context);
|
||||||
val frameRate = context.composition().getTimeline().getFrameRate();
|
final var frameRate = context.composition().getTimeline().getFrameRate();
|
||||||
final double frame;
|
final double frame;
|
||||||
switch (node.operator) {
|
switch (node.operator) {
|
||||||
case SECONDS:
|
case SECONDS:
|
||||||
@ -155,12 +154,12 @@ public class InterpreterVisitor implements ResultVisitor<Value, Context> {
|
|||||||
.orElseThrow(exceptionSupplier);
|
.orElseThrow(exceptionSupplier);
|
||||||
switch (container.type()) {
|
switch (container.type()) {
|
||||||
case Types.MAP: {
|
case Types.MAP: {
|
||||||
val key = node.indices.get(lastIndex).accept(this, context).asString();
|
final var key = node.indices.get(lastIndex).accept(this, context).asString();
|
||||||
((MapValue) container).getMap().put(key, value);
|
((MapValue) container).getMap().put(key, value);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case Types.NODE: {
|
case Types.NODE: {
|
||||||
val key = node.indices.get(lastIndex).accept(this, context).asString();
|
final var key = node.indices.get(lastIndex).accept(this, context).asString();
|
||||||
((NodeValue) container).set(key, value);
|
((NodeValue) container).set(key, value);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
@ -174,18 +173,18 @@ public class InterpreterVisitor implements ResultVisitor<Value, Context> {
|
|||||||
for (Node index : nodes) {
|
for (Node index : nodes) {
|
||||||
switch (container.type()) {
|
switch (container.type()) {
|
||||||
case Types.MAP: {
|
case Types.MAP: {
|
||||||
val key = index.accept(this, context).asString();
|
final var key = index.accept(this, context).asString();
|
||||||
container = ((MapValue) container).getMap().get(key);
|
container = ((MapValue) container).getMap().get(key);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case Types.NODE: {
|
case Types.NODE: {
|
||||||
val key = index.accept(this, context).asString();
|
final var key = index.accept(this, context).asString();
|
||||||
container = ((NodeValue) container).get(key);
|
container = ((NodeValue) container).get(key);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case Types.PROPERTY: {
|
case Types.PROPERTY: {
|
||||||
val key = index.accept(this, context).asString();
|
final var key = index.accept(this, context).asString();
|
||||||
val propertyValue = (PropertyValue) container;
|
final var propertyValue = (PropertyValue) container;
|
||||||
container = propertyValue.getField(key);
|
container = propertyValue.getField(key);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
@ -198,7 +197,7 @@ public class InterpreterVisitor implements ResultVisitor<Value, Context> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Value get(VariableNode node, Context context) {
|
public Value get(VariableNode node, Context context) {
|
||||||
val result = context.variables().get(node.name);
|
final var result = context.variables().get(node.name);
|
||||||
if (result == null)
|
if (result == null)
|
||||||
throw new VariableNotFoundException(node.name, node.start(), node.end());
|
throw new VariableNotFoundException(node.name, node.start(), node.end());
|
||||||
return result;
|
return result;
|
||||||
|
@ -8,7 +8,6 @@ import java.util.Map;
|
|||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import javafx.scene.text.Font;
|
import javafx.scene.text.Font;
|
||||||
import javafx.util.Pair;
|
import javafx.util.Pair;
|
||||||
import lombok.Getter;
|
|
||||||
|
|
||||||
public enum FontAwesome {
|
public enum FontAwesome {
|
||||||
|
|
||||||
@ -28,7 +27,6 @@ public enum FontAwesome {
|
|||||||
SEARCH_MINUS("\uf010", "search-minus"),
|
SEARCH_MINUS("\uf010", "search-minus"),
|
||||||
UNDO("\uf0e2", "undo");
|
UNDO("\uf0e2", "undo");
|
||||||
|
|
||||||
@Getter
|
|
||||||
private final String symbol;
|
private final String symbol;
|
||||||
private final List<String> names;
|
private final List<String> names;
|
||||||
|
|
||||||
@ -41,6 +39,10 @@ public enum FontAwesome {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getSymbol() {
|
||||||
|
return symbol;
|
||||||
|
}
|
||||||
|
|
||||||
public static String getIcon(String name) {
|
public static String getIcon(String name) {
|
||||||
return MAPPING.get(name);
|
return MAPPING.get(name);
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,6 @@ import java.util.function.Function;
|
|||||||
import javafx.scene.Scene;
|
import javafx.scene.Scene;
|
||||||
import javafx.stage.Modality;
|
import javafx.stage.Modality;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
import lombok.val;
|
|
||||||
|
|
||||||
public class RenderPreparer {
|
public class RenderPreparer {
|
||||||
|
|
||||||
@ -71,8 +70,8 @@ public class RenderPreparer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void evaluate() {
|
private void evaluate() {
|
||||||
val parser = new HotaruParser(HotaruLexer.tokenize(source.input));
|
final var parser = new HotaruParser(HotaruLexer.tokenize(source.input));
|
||||||
val program = parser.parse();
|
final var program = parser.parse();
|
||||||
if (parser.getParseErrors().hasErrors()) {
|
if (parser.getParseErrors().hasErrors()) {
|
||||||
throw new RendererException(parser.getParseErrors().toString());
|
throw new RendererException(parser.getParseErrors().toString());
|
||||||
}
|
}
|
||||||
@ -91,10 +90,10 @@ public class RenderPreparer {
|
|||||||
|
|
||||||
public WithStage prepareStage(Stage primaryStage) {
|
public WithStage prepareStage(Stage primaryStage) {
|
||||||
checkCompositionExists();
|
checkCompositionExists();
|
||||||
val stage = new Stage();
|
final var stage = new Stage();
|
||||||
stage.initOwner(primaryStage);
|
stage.initOwner(primaryStage);
|
||||||
stage.initModality(Modality.WINDOW_MODAL);
|
stage.initModality(Modality.WINDOW_MODAL);
|
||||||
val composition = source.context.composition();
|
final var composition = source.context.composition();
|
||||||
stage.setScene(sceneProvider().apply(composition));
|
stage.setScene(sceneProvider().apply(composition));
|
||||||
return new WithStage(composition, stage);
|
return new WithStage(composition, stage);
|
||||||
}
|
}
|
||||||
|
@ -15,13 +15,10 @@ import java.util.concurrent.ExecutorService;
|
|||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import javafx.beans.property.BooleanProperty;
|
import javafx.beans.property.BooleanProperty;
|
||||||
import javafx.concurrent.Task;
|
import javafx.concurrent.Task;
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import lombok.val;
|
|
||||||
import org.fxmisc.richtext.CodeArea;
|
import org.fxmisc.richtext.CodeArea;
|
||||||
import org.fxmisc.richtext.StyleSpans;
|
import org.fxmisc.richtext.model.StyleSpans;
|
||||||
import org.fxmisc.richtext.StyleSpansBuilder;
|
import org.fxmisc.richtext.model.StyleSpansBuilder;
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class SyntaxHighlighter {
|
public class SyntaxHighlighter {
|
||||||
|
|
||||||
private final CodeArea editor;
|
private final CodeArea editor;
|
||||||
@ -29,6 +26,11 @@ public class SyntaxHighlighter {
|
|||||||
private Set<String> nodeFunctions;
|
private Set<String> nodeFunctions;
|
||||||
private Map<HotaruTokenId, String> operatorClasses;
|
private Map<HotaruTokenId, String> operatorClasses;
|
||||||
|
|
||||||
|
public SyntaxHighlighter(CodeArea editor, ExecutorService executor) {
|
||||||
|
this.editor = editor;
|
||||||
|
this.executor = executor;
|
||||||
|
}
|
||||||
|
|
||||||
public void init(BooleanProperty enabledProperty) {
|
public void init(BooleanProperty enabledProperty) {
|
||||||
operatorClasses = new HashMap<>();
|
operatorClasses = new HashMap<>();
|
||||||
operatorClasses.put(HotaruTokenId.AT, "keyframes-extractor");
|
operatorClasses.put(HotaruTokenId.AT, "keyframes-extractor");
|
||||||
@ -51,13 +53,13 @@ public class SyntaxHighlighter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Task<StyleSpans<Collection<String>>> computeHighlightingAsync() {
|
private Task<StyleSpans<Collection<String>>> computeHighlightingAsync() {
|
||||||
val text = editor.getText();
|
final var text = editor.getText();
|
||||||
val task = new Task<StyleSpans<Collection<String>>>() {
|
final var task = new Task<StyleSpans<Collection<String>>>() {
|
||||||
@Override
|
@Override
|
||||||
protected StyleSpans<Collection<String>> call() throws Exception {
|
protected StyleSpans<Collection<String>> call() throws Exception {
|
||||||
val spans = new StyleSpansBuilder<Collection<String>>();
|
final var spans = new StyleSpansBuilder<Collection<String>>();
|
||||||
for (val t : new HotaruLexer(text).tokenize()) {
|
for (final var t : new HotaruLexer(text).tokenize()) {
|
||||||
val category = t.getType().getPrimaryCategory();
|
final var category = t.getType().getPrimaryCategory();
|
||||||
switch (category) {
|
switch (category) {
|
||||||
case "string":
|
case "string":
|
||||||
case "keyword":
|
case "keyword":
|
||||||
@ -75,7 +77,7 @@ public class SyntaxHighlighter {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case "operator":
|
case "operator":
|
||||||
val className = operatorClasses.get(t.getType());
|
final var className = operatorClasses.get(t.getType());
|
||||||
if (className != null) {
|
if (className != null) {
|
||||||
spans.add(Collections.singleton(className), t.getLength());
|
spans.add(Collections.singleton(className), t.getLength());
|
||||||
} else {
|
} else {
|
||||||
|
@ -20,7 +20,6 @@ import java.util.LinkedHashMap;
|
|||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.beans.binding.Bindings;
|
|
||||||
import javafx.beans.binding.BooleanBinding;
|
import javafx.beans.binding.BooleanBinding;
|
||||||
import javafx.event.ActionEvent;
|
import javafx.event.ActionEvent;
|
||||||
import javafx.event.EventHandler;
|
import javafx.event.EventHandler;
|
||||||
@ -43,13 +42,14 @@ import javafx.stage.Modality;
|
|||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
import javafx.stage.WindowEvent;
|
import javafx.stage.WindowEvent;
|
||||||
import javafx.util.Duration;
|
import javafx.util.Duration;
|
||||||
import lombok.val;
|
|
||||||
import org.fxmisc.richtext.CodeArea;
|
import org.fxmisc.richtext.CodeArea;
|
||||||
import org.fxmisc.richtext.LineNumberFactory;
|
import org.fxmisc.richtext.LineNumberFactory;
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public class EditorController implements Initializable, DocumentListener {
|
public class EditorController implements Initializable, DocumentListener {
|
||||||
|
|
||||||
|
private static final int DEFAULT_FONT_SIZE = 14;
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private Menu examplesMenu;
|
private Menu examplesMenu;
|
||||||
|
|
||||||
@ -73,6 +73,7 @@ public class EditorController implements Initializable, DocumentListener {
|
|||||||
private Stage primaryStage;
|
private Stage primaryStage;
|
||||||
private SyntaxHighlighter syntaxHighlighter;
|
private SyntaxHighlighter syntaxHighlighter;
|
||||||
private DocumentManager documentManager;
|
private DocumentManager documentManager;
|
||||||
|
private int fontSize = DEFAULT_FONT_SIZE;
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private void handleMenuNew(ActionEvent event) {
|
private void handleMenuNew(ActionEvent event) {
|
||||||
@ -83,7 +84,7 @@ public class EditorController implements Initializable, DocumentListener {
|
|||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private void handleMenuOpen(ActionEvent event) {
|
private void handleMenuOpen(ActionEvent event) {
|
||||||
val isOpened = documentManager.open(primaryStage, editor::replaceText);
|
final var isOpened = documentManager.open(primaryStage, editor::replaceText);
|
||||||
if (isOpened) {
|
if (isOpened) {
|
||||||
updateTitle();
|
updateTitle();
|
||||||
}
|
}
|
||||||
@ -120,13 +121,13 @@ public class EditorController implements Initializable, DocumentListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean confirmExit() {
|
private boolean confirmExit() {
|
||||||
val alert = new Alert(Alert.AlertType.CONFIRMATION);
|
final var alert = new Alert(Alert.AlertType.CONFIRMATION);
|
||||||
alert.setTitle("Exit");
|
alert.setTitle("Exit");
|
||||||
alert.setHeaderText("Are you sure you want to exit?");
|
alert.setHeaderText("Are you sure you want to exit?");
|
||||||
alert.initOwner(primaryStage);
|
alert.initOwner(primaryStage);
|
||||||
alert.initModality(Modality.APPLICATION_MODAL);
|
alert.initModality(Modality.APPLICATION_MODAL);
|
||||||
alert.getDialogPane().setContent(new Group());
|
alert.getDialogPane().setContent(new Group());
|
||||||
val icon = new FontAwesomeIcon(FontAwesome.QUESTION_CIRCLE);
|
final var icon = new FontAwesomeIcon(FontAwesome.QUESTION_CIRCLE);
|
||||||
alert.getDialogPane().setGraphic(icon);
|
alert.getDialogPane().setGraphic(icon);
|
||||||
return alert.showAndWait()
|
return alert.showAndWait()
|
||||||
.filter(b -> b == ButtonType.OK)
|
.filter(b -> b == ButtonType.OK)
|
||||||
@ -144,22 +145,22 @@ public class EditorController implements Initializable, DocumentListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void changeFontSize(int delta) {
|
private void changeFontSize(int delta) {
|
||||||
if (editor.getFont() == null) return;
|
final int newSize = fontSize + delta;
|
||||||
val newSize = (int) editor.getFont().getSize() + delta;
|
|
||||||
if (8 > newSize || newSize > 40) return;
|
if (8 > newSize || newSize > 40) return;
|
||||||
|
fontSize = newSize;
|
||||||
editor.setStyle("-fx-font-size: " + newSize + "px");
|
editor.setStyle("-fx-font-size: " + newSize + "px");
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private void handleMenuAbout(ActionEvent event) {
|
private void handleMenuAbout(ActionEvent event) {
|
||||||
val stage = new Stage();
|
final var stage = new Stage();
|
||||||
stage.setTitle("About");
|
stage.setTitle("About");
|
||||||
stage.setResizable(false);
|
stage.setResizable(false);
|
||||||
stage.initOwner(primaryStage);
|
stage.initOwner(primaryStage);
|
||||||
stage.initModality(Modality.WINDOW_MODAL);
|
stage.initModality(Modality.WINDOW_MODAL);
|
||||||
try {
|
try {
|
||||||
val loader = new FXMLLoader(getClass().getResource("/fxml/About.fxml"));
|
final var loader = new FXMLLoader(getClass().getResource("/fxml/About.fxml"));
|
||||||
val scene = new Scene(loader.load());
|
final var scene = new Scene(loader.load());
|
||||||
scene.getStylesheets().addAll(
|
scene.getStylesheets().addAll(
|
||||||
getClass().getResource("/styles/theme-dark.css").toExternalForm(),
|
getClass().getResource("/styles/theme-dark.css").toExternalForm(),
|
||||||
getClass().getResource("/styles/about.css").toExternalForm()
|
getClass().getResource("/styles/about.css").toExternalForm()
|
||||||
@ -174,7 +175,7 @@ public class EditorController implements Initializable, DocumentListener {
|
|||||||
@FXML
|
@FXML
|
||||||
private void handleMenuPlay(ActionEvent event) {
|
private void handleMenuPlay(ActionEvent event) {
|
||||||
log.setText("");
|
log.setText("");
|
||||||
val input = editor.getText();
|
final var input = editor.getText();
|
||||||
if (input.isEmpty()) {
|
if (input.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -185,14 +186,14 @@ public class EditorController implements Initializable, DocumentListener {
|
|||||||
.evaluateWithRuntimeBundle()
|
.evaluateWithRuntimeBundle()
|
||||||
.prepareStage(primaryStage)
|
.prepareStage(primaryStage)
|
||||||
.peek((stage, composition) -> {
|
.peek((stage, composition) -> {
|
||||||
val timeline = composition.getTimeline();
|
final var timeline = composition.getTimeline();
|
||||||
timeline.getFxTimeline().currentTimeProperty().addListener((o, oldValue, d) -> {
|
timeline.getFxTimeline().currentTimeProperty().addListener((o, oldValue, d) -> {
|
||||||
val min = (int) d.toMinutes();
|
final var min = (int) d.toMinutes();
|
||||||
val durationSec = d.subtract(Duration.minutes(min));
|
final var durationSec = d.subtract(Duration.minutes(min));
|
||||||
val sec = (int) durationSec.toSeconds();
|
final var sec = (int) durationSec.toSeconds();
|
||||||
val durationMs = durationSec.subtract(Duration.seconds(sec));
|
final var durationMs = durationSec.subtract(Duration.seconds(sec));
|
||||||
val frame = (int) (durationMs.toMillis() * timeline.getFrameRate() / 1000d);
|
final var frame = (int) (durationMs.toMillis() * timeline.getFrameRate() / 1000d);
|
||||||
val allFrame = (int) (d.toMillis() * timeline.getFrameRate() / 1000d);
|
final var allFrame = (int) (d.toMillis() * timeline.getFrameRate() / 1000d);
|
||||||
stage.setTitle(String.format("%02d:%02d.%02d %d", min, sec, frame, allFrame));
|
stage.setTitle(String.format("%02d:%02d.%02d %d", min, sec, frame, allFrame));
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -209,7 +210,7 @@ public class EditorController implements Initializable, DocumentListener {
|
|||||||
@FXML
|
@FXML
|
||||||
private void handleMenuRender(ActionEvent event) {
|
private void handleMenuRender(ActionEvent event) {
|
||||||
log.setText("");
|
log.setText("");
|
||||||
val input = editor.getText();
|
final var input = editor.getText();
|
||||||
if (input.isEmpty()) {
|
if (input.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -220,18 +221,18 @@ public class EditorController implements Initializable, DocumentListener {
|
|||||||
.evaluateForRender()
|
.evaluateForRender()
|
||||||
.prepareStage(primaryStage)
|
.prepareStage(primaryStage)
|
||||||
.peek((stage, composition) -> {
|
.peek((stage, composition) -> {
|
||||||
val chooser = new DirectoryChooser();
|
final var chooser = new DirectoryChooser();
|
||||||
chooser.setTitle("Choose directory for rendering frames");
|
chooser.setTitle("Choose directory for rendering frames");
|
||||||
val directory = chooser.showDialog(primaryStage);
|
final var directory = chooser.showDialog(primaryStage);
|
||||||
if (directory == null || !directory.exists() || !directory.isDirectory()) {
|
if (directory == null || !directory.exists() || !directory.isDirectory()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
val fxTimeline = composition.getTimeline().getFxTimeline();
|
final var fxTimeline = composition.getTimeline().getFxTimeline();
|
||||||
stage.setOnShown(e -> {
|
stage.setOnShown(e -> {
|
||||||
fxTimeline.playFromStart();
|
fxTimeline.playFromStart();
|
||||||
fxTimeline.pause();
|
fxTimeline.pause();
|
||||||
val task = new RenderTask(directory, composition, stage.getScene());
|
final var task = new RenderTask(directory, composition, stage.getScene());
|
||||||
task.messageProperty().addListener(ev -> {
|
task.messageProperty().addListener(ev -> {
|
||||||
stage.setTitle(task.getMessage());
|
stage.setTitle(task.getMessage());
|
||||||
});
|
});
|
||||||
@ -257,6 +258,7 @@ public class EditorController implements Initializable, DocumentListener {
|
|||||||
documentManager = new FileManager();
|
documentManager = new FileManager();
|
||||||
populateExamples();
|
populateExamples();
|
||||||
initSyntaxHighlighter();
|
initSyntaxHighlighter();
|
||||||
|
changeFontSize(0);
|
||||||
initUndoRedo();
|
initUndoRedo();
|
||||||
initCopyCutPaste();
|
initCopyCutPaste();
|
||||||
openExample();
|
openExample();
|
||||||
@ -266,7 +268,7 @@ public class EditorController implements Initializable, DocumentListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void populateExamples() {
|
private void populateExamples() {
|
||||||
val map = new LinkedHashMap<String, String>();
|
final var map = new LinkedHashMap<String, String>();
|
||||||
map.put("HotaruFX Logo", "hotarufx-logo.hfx");
|
map.put("HotaruFX Logo", "hotarufx-logo.hfx");
|
||||||
map.put("Font Awesome Icons", "font-awesome.hfx");
|
map.put("Font Awesome Icons", "font-awesome.hfx");
|
||||||
map.put("HSV Color", "hsv.hfx");
|
map.put("HSV Color", "hsv.hfx");
|
||||||
@ -280,19 +282,19 @@ public class EditorController implements Initializable, DocumentListener {
|
|||||||
map.put("Blend Modes", "blend-modes.hfx");
|
map.put("Blend Modes", "blend-modes.hfx");
|
||||||
map.put("Stroke Ants", "stroke-ants.hfx");
|
map.put("Stroke Ants", "stroke-ants.hfx");
|
||||||
examplesMenu.getItems().clear();
|
examplesMenu.getItems().clear();
|
||||||
for (val entry : map.entrySet()) {
|
for (final var entry : map.entrySet()) {
|
||||||
val item = new MenuItem(entry.getKey());
|
final var item = new MenuItem(entry.getKey());
|
||||||
item.setOnAction(e -> openExample("/examples/" + entry.getValue()));
|
item.setOnAction(e -> openExample("/examples/" + entry.getValue()));
|
||||||
examplesMenu.getItems().add(item);
|
examplesMenu.getItems().add(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initSyntaxHighlighter() {
|
private void initSyntaxHighlighter() {
|
||||||
val highlightProperty = syntaxHighlightingItem.selectedProperty();
|
final var highlightProperty = syntaxHighlightingItem.selectedProperty();
|
||||||
highlightProperty.addListener((observable, oldValue, highlightEnabled) -> {
|
highlightProperty.addListener((observable, oldValue, highlightEnabled) -> {
|
||||||
if (highlightEnabled) {
|
if (highlightEnabled) {
|
||||||
// create event to reinit highlighter
|
// create event to reinit highlighter
|
||||||
val pos = editor.getCaretPosition();
|
final var pos = editor.getCaretPosition();
|
||||||
editor.insertText(pos, " ");
|
editor.insertText(pos, " ");
|
||||||
editor.replaceText(pos, pos + 1, "");
|
editor.replaceText(pos, pos + 1, "");
|
||||||
} else {
|
} else {
|
||||||
@ -304,16 +306,14 @@ public class EditorController implements Initializable, DocumentListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void initUndoRedo() {
|
private void initUndoRedo() {
|
||||||
undoButton.disableProperty().bind(
|
undoButton.disableProperty().bind(editor.undoAvailableProperty().map(x -> !x));
|
||||||
Bindings.not(editor.undoAvailableProperty()));
|
redoButton.disableProperty().bind(editor.redoAvailableProperty().map(x -> !x));
|
||||||
redoButton.disableProperty().bind(
|
|
||||||
Bindings.not(editor.redoAvailableProperty()));
|
|
||||||
undoButton.setOnAction(editorAction(editor::undo));
|
undoButton.setOnAction(editorAction(editor::undo));
|
||||||
redoButton.setOnAction(editorAction(editor::redo));
|
redoButton.setOnAction(editorAction(editor::redo));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initCopyCutPaste() {
|
private void initCopyCutPaste() {
|
||||||
val selectionEmpty = new BooleanBinding() {
|
final var selectionEmpty = new BooleanBinding() {
|
||||||
{ bind(editor.selectionProperty()); }
|
{ bind(editor.selectionProperty()); }
|
||||||
@Override
|
@Override
|
||||||
protected boolean computeValue() {
|
protected boolean computeValue() {
|
||||||
@ -367,7 +367,7 @@ public class EditorController implements Initializable, DocumentListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void openExample(String path) {
|
private void openExample(String path) {
|
||||||
val content = (path != null) ? readProgram(path) : fallbackProgram();
|
final var content = (path != null) ? readProgram(path) : fallbackProgram();
|
||||||
editor.replaceText(content);
|
editor.replaceText(content);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,7 +11,6 @@ import javafx.scene.Scene;
|
|||||||
import javafx.scene.image.WritableImage;
|
import javafx.scene.image.WritableImage;
|
||||||
import javafx.util.Duration;
|
import javafx.util.Duration;
|
||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
import lombok.val;
|
|
||||||
|
|
||||||
public class RenderTask extends Task<Boolean> {
|
public class RenderTask extends Task<Boolean> {
|
||||||
|
|
||||||
@ -31,25 +30,25 @@ public class RenderTask extends Task<Boolean> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Boolean call() throws Exception {
|
protected Boolean call() throws Exception {
|
||||||
val fxTimeline = timeLine.getFxTimeline();
|
final var fxTimeline = timeLine.getFxTimeline();
|
||||||
|
|
||||||
val totalFrames = toFrame(fxTimeline.getTotalDuration());
|
final var totalFrames = toFrame(fxTimeline.getTotalDuration());
|
||||||
int frame = 0;
|
int frame = 0;
|
||||||
while (frame < totalFrames) {
|
while (frame < totalFrames) {
|
||||||
updateProgress(frame, totalFrames);
|
updateProgress(frame, totalFrames);
|
||||||
updateMessage(String.format("%d / %d", frame + 1, totalFrames));
|
updateMessage(String.format("%d / %d", frame + 1, totalFrames));
|
||||||
fxTimeline.jumpTo(toDuration(frame));
|
fxTimeline.jumpTo(toDuration(frame));
|
||||||
|
|
||||||
val image = newImage();
|
final var image = newImage();
|
||||||
|
|
||||||
val latch = new CountDownLatch(1);
|
final var latch = new CountDownLatch(1);
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
scene.snapshot(image);
|
scene.snapshot(image);
|
||||||
latch.countDown();
|
latch.countDown();
|
||||||
});
|
});
|
||||||
latch.await();
|
latch.await();
|
||||||
|
|
||||||
val file = new File(directory, String.format("frame_%05d.png", frame + 1));
|
final var file = new File(directory, String.format("frame_%05d.png", frame + 1));
|
||||||
ImageIO.write(SwingFXUtils.fromFXImage(image, null), "png", file);
|
ImageIO.write(SwingFXUtils.fromFXImage(image, null), "png", file);
|
||||||
frame++;
|
frame++;
|
||||||
}
|
}
|
||||||
|
@ -5,25 +5,18 @@ import javafx.scene.Scene;
|
|||||||
import javafx.scene.input.KeyCode;
|
import javafx.scene.input.KeyCode;
|
||||||
import javafx.scene.paint.Color;
|
import javafx.scene.paint.Color;
|
||||||
import javafx.scene.paint.Paint;
|
import javafx.scene.paint.Paint;
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.val;
|
|
||||||
|
|
||||||
public class Composition {
|
public class Composition {
|
||||||
|
|
||||||
@Getter
|
|
||||||
private final int
|
private final int
|
||||||
virtualWidth, virtualHeight,
|
virtualWidth, virtualHeight,
|
||||||
sceneWidth, sceneHeight;
|
sceneWidth, sceneHeight;
|
||||||
@Getter
|
|
||||||
private final double factor;
|
private final double factor;
|
||||||
|
|
||||||
@Getter
|
|
||||||
private final TimeLine timeline;
|
private final TimeLine timeline;
|
||||||
|
|
||||||
@Getter
|
|
||||||
private final VirtualScene scene;
|
private final VirtualScene scene;
|
||||||
|
|
||||||
@Getter
|
|
||||||
private final Paint background;
|
private final Paint background;
|
||||||
|
|
||||||
public Composition() {
|
public Composition() {
|
||||||
@ -53,17 +46,49 @@ public class Composition {
|
|||||||
scene = newScene();
|
scene = newScene();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getVirtualWidth() {
|
||||||
|
return virtualWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getVirtualHeight() {
|
||||||
|
return virtualHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSceneWidth() {
|
||||||
|
return sceneWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSceneHeight() {
|
||||||
|
return sceneHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getFactor() {
|
||||||
|
return factor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TimeLine getTimeline() {
|
||||||
|
return timeline;
|
||||||
|
}
|
||||||
|
|
||||||
|
public VirtualScene getScene() {
|
||||||
|
return scene;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Paint getBackground() {
|
||||||
|
return background;
|
||||||
|
}
|
||||||
|
|
||||||
private VirtualScene newScene() {
|
private VirtualScene newScene() {
|
||||||
val group = new NodesGroup(sceneWidth, sceneHeight);
|
final var group = new NodesGroup(sceneWidth, sceneHeight);
|
||||||
group.setScaleX(1d / factor);
|
group.setScaleX(1d / factor);
|
||||||
group.setScaleY(1d / factor);
|
group.setScaleY(1d / factor);
|
||||||
group.setTranslateX(sceneWidth / 2);
|
group.setTranslateX(sceneWidth / 2f);
|
||||||
group.setTranslateY(sceneHeight / 2);
|
group.setTranslateY(sceneHeight / 2f);
|
||||||
return new VirtualScene(group, virtualWidth, virtualHeight);
|
return new VirtualScene(group, virtualWidth, virtualHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Scene producePreviewScene() {
|
public Scene producePreviewScene() {
|
||||||
val fxScene = new Scene(scene.getGroup(), sceneWidth, sceneHeight, background);
|
final var fxScene = new Scene(scene.getGroup(), sceneWidth, sceneHeight, background);
|
||||||
fxScene.setOnKeyPressed(e -> {
|
fxScene.setOnKeyPressed(e -> {
|
||||||
switch (e.getCode()) {
|
switch (e.getCode()) {
|
||||||
case SPACE:
|
case SPACE:
|
||||||
@ -92,8 +117,6 @@ public class Composition {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Scene produceRendererScene() {
|
public Scene produceRendererScene() {
|
||||||
val fxScene = new Scene(scene.getGroup(), sceneWidth, sceneHeight, background);
|
return new Scene(scene.getGroup(), sceneWidth, sceneHeight, background);
|
||||||
|
|
||||||
return fxScene;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,38 @@
|
|||||||
package com.annimon.hotarufx.visual;
|
package com.annimon.hotarufx.visual;
|
||||||
|
|
||||||
import lombok.EqualsAndHashCode;
|
import java.util.Objects;
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
|
|
||||||
@RequiredArgsConstructor(staticName="of")
|
|
||||||
@EqualsAndHashCode
|
|
||||||
public class KeyFrame implements Comparable<KeyFrame> {
|
public class KeyFrame implements Comparable<KeyFrame> {
|
||||||
|
|
||||||
@Getter
|
public static KeyFrame of(int frame) {
|
||||||
|
return new KeyFrame(frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
private KeyFrame(int frame) {
|
||||||
|
this.frame = frame;
|
||||||
|
}
|
||||||
|
|
||||||
private final int frame;
|
private final int frame;
|
||||||
|
|
||||||
|
public int getFrame() {
|
||||||
|
return frame;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int compareTo(KeyFrame o) {
|
public int compareTo(KeyFrame o) {
|
||||||
return Integer.compare(frame, o.frame);
|
return Integer.compare(frame, o.frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
KeyFrame keyFrame = (KeyFrame) o;
|
||||||
|
return frame == keyFrame.frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(frame);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,26 @@
|
|||||||
package com.annimon.hotarufx.visual;
|
package com.annimon.hotarufx.visual;
|
||||||
|
|
||||||
import javafx.animation.Interpolator;
|
import javafx.animation.Interpolator;
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
|
|
||||||
@Getter
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class KeyFrameValue<T> {
|
public class KeyFrameValue<T> {
|
||||||
|
|
||||||
private final T value;
|
private final T value;
|
||||||
private final Interpolator interpolator;
|
private final Interpolator interpolator;
|
||||||
|
|
||||||
|
public KeyFrameValue(T value, Interpolator interpolator) {
|
||||||
|
this.value = value;
|
||||||
|
this.interpolator = interpolator;
|
||||||
|
}
|
||||||
|
|
||||||
public KeyFrameValue(T value) {
|
public KeyFrameValue(T value) {
|
||||||
this(value, Interpolator.LINEAR);
|
this(value, Interpolator.LINEAR);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public T getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Interpolator getInterpolator() {
|
||||||
|
return interpolator;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,22 @@
|
|||||||
package com.annimon.hotarufx.visual;
|
package com.annimon.hotarufx.visual;
|
||||||
|
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public final class Property {
|
public final class Property {
|
||||||
|
|
||||||
@Getter
|
|
||||||
private final PropertyType type;
|
private final PropertyType type;
|
||||||
|
|
||||||
@Getter
|
|
||||||
private final Supplier<PropertyTimeline<?>> property;
|
private final Supplier<PropertyTimeline<?>> property;
|
||||||
|
|
||||||
|
public Property(PropertyType type, Supplier<PropertyTimeline<?>> property) {
|
||||||
|
this.type = type;
|
||||||
|
this.property = property;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PropertyType getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Supplier<PropertyTimeline<?>> getProperty() {
|
||||||
|
return property;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,9 +4,7 @@ import java.util.Map;
|
|||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
import javafx.animation.Interpolator;
|
import javafx.animation.Interpolator;
|
||||||
import javafx.beans.value.WritableValue;
|
import javafx.beans.value.WritableValue;
|
||||||
import lombok.Getter;
|
|
||||||
|
|
||||||
@Getter
|
|
||||||
public class PropertyTimeline<T> {
|
public class PropertyTimeline<T> {
|
||||||
|
|
||||||
private final WritableValue<T> property;
|
private final WritableValue<T> property;
|
||||||
@ -17,6 +15,14 @@ public class PropertyTimeline<T> {
|
|||||||
keyFrames = new TreeMap<>();
|
keyFrames = new TreeMap<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public WritableValue<T> getProperty() {
|
||||||
|
return property;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<KeyFrame, KeyFrameValue<T>> getKeyFrames() {
|
||||||
|
return keyFrames;
|
||||||
|
}
|
||||||
|
|
||||||
public PropertyTimeline<T> add(KeyFrame keyFrame, T value) {
|
public PropertyTimeline<T> add(KeyFrame keyFrame, T value) {
|
||||||
keyFrames.put(keyFrame, new KeyFrameValue<>(value));
|
keyFrames.put(keyFrame, new KeyFrameValue<>(value));
|
||||||
return this;
|
return this;
|
||||||
|
@ -13,11 +13,7 @@ import java.util.function.Function;
|
|||||||
import javafx.scene.Node;
|
import javafx.scene.Node;
|
||||||
import javafx.scene.paint.Color;
|
import javafx.scene.paint.Color;
|
||||||
import javafx.scene.text.Font;
|
import javafx.scene.text.Font;
|
||||||
import lombok.AccessLevel;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
|
|
||||||
@SuppressWarnings("ConstantConditions")
|
|
||||||
@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
|
|
||||||
public enum PropertyType {
|
public enum PropertyType {
|
||||||
|
|
||||||
BOOLEAN(Value::asBoolean, o -> NumberValue.fromBoolean(Boolean.TRUE.equals(o))),
|
BOOLEAN(Value::asBoolean, o -> NumberValue.fromBoolean(Boolean.TRUE.equals(o))),
|
||||||
@ -28,6 +24,11 @@ public enum PropertyType {
|
|||||||
PAINT(v -> Color.valueOf(v.asString()), o -> new StringValue(o.toString())),
|
PAINT(v -> Color.valueOf(v.asString()), o -> new StringValue(o.toString())),
|
||||||
FONT(toFont(), object -> new FontValue((Font) object));
|
FONT(toFont(), object -> new FontValue((Font) object));
|
||||||
|
|
||||||
|
PropertyType(Function<Value, Object> fromHFX, Function<Object, Value> toHFX) {
|
||||||
|
this.fromHFX = fromHFX;
|
||||||
|
this.toHFX = toHFX;
|
||||||
|
}
|
||||||
|
|
||||||
private final Function<Value, Object> fromHFX;
|
private final Function<Value, Object> fromHFX;
|
||||||
private final Function<Object, Value> toHFX;
|
private final Function<Object, Value> toHFX;
|
||||||
|
|
||||||
|
@ -3,15 +3,11 @@ package com.annimon.hotarufx.visual;
|
|||||||
import javafx.animation.KeyValue;
|
import javafx.animation.KeyValue;
|
||||||
import javafx.animation.Timeline;
|
import javafx.animation.Timeline;
|
||||||
import javafx.util.Duration;
|
import javafx.util.Duration;
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.val;
|
|
||||||
|
|
||||||
public class TimeLine {
|
public class TimeLine {
|
||||||
|
|
||||||
@Getter
|
|
||||||
private final double frameRate;
|
private final double frameRate;
|
||||||
|
|
||||||
@Getter
|
|
||||||
private final Timeline fxTimeline;
|
private final Timeline fxTimeline;
|
||||||
|
|
||||||
public TimeLine(double frameRate) {
|
public TimeLine(double frameRate) {
|
||||||
@ -19,6 +15,14 @@ public class TimeLine {
|
|||||||
fxTimeline = new Timeline(frameRate);
|
fxTimeline = new Timeline(frameRate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public double getFrameRate() {
|
||||||
|
return frameRate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Timeline getFxTimeline() {
|
||||||
|
return fxTimeline;
|
||||||
|
}
|
||||||
|
|
||||||
public void addKeyFrame(KeyFrame keyFrame, KeyValue fxKeyValue) {
|
public void addKeyFrame(KeyFrame keyFrame, KeyValue fxKeyValue) {
|
||||||
fxTimeline.getKeyFrames().add(new javafx.animation.KeyFrame(
|
fxTimeline.getKeyFrames().add(new javafx.animation.KeyFrame(
|
||||||
duration(keyFrame), fxKeyValue));
|
duration(keyFrame), fxKeyValue));
|
||||||
@ -44,14 +48,14 @@ public class TimeLine {
|
|||||||
|
|
||||||
public void seekFrame(final int value) {
|
public void seekFrame(final int value) {
|
||||||
fxTimeline.pause();
|
fxTimeline.pause();
|
||||||
val offset = Duration.millis(1000d * Math.abs(value) / frameRate);
|
final var offset = Duration.millis(1000d * Math.abs(value) / frameRate);
|
||||||
val now = fxTimeline.getCurrentTime();
|
final var now = fxTimeline.getCurrentTime();
|
||||||
val newDuration = value > 0 ? now.add(offset) : now.subtract(offset);
|
final var newDuration = value > 0 ? now.add(offset) : now.subtract(offset);
|
||||||
fxTimeline.jumpTo(newDuration);
|
fxTimeline.jumpTo(newDuration);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void seek(final int sec) {
|
public void seek(final int sec) {
|
||||||
val now = fxTimeline.getCurrentTime();
|
final var now = fxTimeline.getCurrentTime();
|
||||||
fxTimeline.jumpTo(now.add(Duration.seconds(sec)));
|
fxTimeline.jumpTo(now.add(Duration.seconds(sec)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,18 +2,31 @@ package com.annimon.hotarufx.visual;
|
|||||||
|
|
||||||
import com.annimon.hotarufx.ui.control.NodesGroup;
|
import com.annimon.hotarufx.ui.control.NodesGroup;
|
||||||
import javafx.scene.Node;
|
import javafx.scene.Node;
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class VirtualScene {
|
public class VirtualScene {
|
||||||
|
|
||||||
@Getter
|
|
||||||
private final NodesGroup group;
|
private final NodesGroup group;
|
||||||
|
|
||||||
@Getter
|
|
||||||
private final int virtualWidth, virtualHeight;
|
private final int virtualWidth, virtualHeight;
|
||||||
|
|
||||||
|
public VirtualScene(NodesGroup group, int virtualWidth, int virtualHeight) {
|
||||||
|
this.group = group;
|
||||||
|
this.virtualWidth = virtualWidth;
|
||||||
|
this.virtualHeight = virtualHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public NodesGroup getGroup() {
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getVirtualWidth() {
|
||||||
|
return virtualWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getVirtualHeight() {
|
||||||
|
return virtualHeight;
|
||||||
|
}
|
||||||
|
|
||||||
public void add(Node node) {
|
public void add(Node node) {
|
||||||
group.getChildren().add(node);
|
group.getChildren().add(node);
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,6 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import javafx.scene.Group;
|
import javafx.scene.Group;
|
||||||
import lombok.val;
|
|
||||||
import static com.annimon.hotarufx.visual.PropertyType.BOOLEAN;
|
import static com.annimon.hotarufx.visual.PropertyType.BOOLEAN;
|
||||||
|
|
||||||
public class GroupNode extends ObjectNode {
|
public class GroupNode extends ObjectNode {
|
||||||
@ -27,7 +26,7 @@ public class GroupNode extends ObjectNode {
|
|||||||
super(group);
|
super(group);
|
||||||
this.group = group;
|
this.group = group;
|
||||||
this.nodes = new ArrayList<>(nodes);
|
this.nodes = new ArrayList<>(nodes);
|
||||||
val fxNodes = nodes.stream()
|
final var fxNodes = nodes.stream()
|
||||||
.map(ObjectNode::getFxNode)
|
.map(ObjectNode::getFxNode)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
group.getChildren().addAll(fxNodes);
|
group.getChildren().addAll(fxNodes);
|
||||||
|
@ -14,9 +14,6 @@ import javafx.beans.value.WritableValue;
|
|||||||
import javafx.scene.Node;
|
import javafx.scene.Node;
|
||||||
import javafx.scene.effect.BlendMode;
|
import javafx.scene.effect.BlendMode;
|
||||||
import javafx.util.StringConverter;
|
import javafx.util.StringConverter;
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.Setter;
|
|
||||||
import lombok.val;
|
|
||||||
import static com.annimon.hotarufx.visual.PropertyType.*;
|
import static com.annimon.hotarufx.visual.PropertyType.*;
|
||||||
|
|
||||||
public abstract class ObjectNode {
|
public abstract class ObjectNode {
|
||||||
@ -31,7 +28,6 @@ public abstract class ObjectNode {
|
|||||||
private PropertyTimelineHolder<Number> scaleX, scaleY, scaleZ;
|
private PropertyTimelineHolder<Number> scaleX, scaleY, scaleZ;
|
||||||
private PropertyTimelineHolder<Number> layoutX, layoutY;
|
private PropertyTimelineHolder<Number> layoutX, layoutY;
|
||||||
private PropertyTimelineHolder<String> blendMode;
|
private PropertyTimelineHolder<String> blendMode;
|
||||||
@Getter @Setter
|
|
||||||
private boolean isRenderable;
|
private boolean isRenderable;
|
||||||
|
|
||||||
public ObjectNode(Node node) {
|
public ObjectNode(Node node) {
|
||||||
@ -57,6 +53,14 @@ public abstract class ObjectNode {
|
|||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isRenderable() {
|
||||||
|
return isRenderable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRenderable(boolean renderable) {
|
||||||
|
isRenderable = renderable;
|
||||||
|
}
|
||||||
|
|
||||||
public PropertyTimeline<Boolean> visibleProperty() {
|
public PropertyTimeline<Boolean> visibleProperty() {
|
||||||
return visible.setIfEmptyThenGet(node::visibleProperty);
|
return visible.setIfEmptyThenGet(node::visibleProperty);
|
||||||
}
|
}
|
||||||
@ -155,7 +159,7 @@ public abstract class ObjectNode {
|
|||||||
|
|
||||||
protected <T extends Enum<T>> Supplier<WritableValue<String>> enumToString(Class<T> enumClass, ObjectProperty<T> property) {
|
protected <T extends Enum<T>> Supplier<WritableValue<String>> enumToString(Class<T> enumClass, ObjectProperty<T> property) {
|
||||||
return () -> {
|
return () -> {
|
||||||
val stringProperty = new SimpleStringProperty();
|
final var stringProperty = new SimpleStringProperty();
|
||||||
stringProperty.bindBidirectional(property, new StringConverter<T>() {
|
stringProperty.bindBidirectional(property, new StringConverter<T>() {
|
||||||
@Override
|
@Override
|
||||||
public String toString(T object) {
|
public String toString(T object) {
|
||||||
@ -174,7 +178,7 @@ public abstract class ObjectNode {
|
|||||||
return Enum.valueOf(enumClass, string);
|
return Enum.valueOf(enumClass, string);
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
try {
|
try {
|
||||||
val number = (int) Double.parseDouble(string);
|
final var number = (int) Double.parseDouble(string);
|
||||||
return enumClass.cast(EnumSet.allOf(enumClass).toArray()[number]);
|
return enumClass.cast(EnumSet.allOf(enumClass).toArray()[number]);
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
throw new HotaruRuntimeException("No constant " + string
|
throw new HotaruRuntimeException("No constant " + string
|
||||||
|
@ -3,13 +3,15 @@ package com.annimon.hotarufx.visual.visitors;
|
|||||||
import com.annimon.hotarufx.visual.TimeLine;
|
import com.annimon.hotarufx.visual.TimeLine;
|
||||||
import com.annimon.hotarufx.visual.VirtualScene;
|
import com.annimon.hotarufx.visual.VirtualScene;
|
||||||
import com.annimon.hotarufx.visual.objects.*;
|
import com.annimon.hotarufx.visual.objects.*;
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class RenderVisitor implements NodeVisitor<Void, VirtualScene> {
|
public class RenderVisitor implements NodeVisitor<Void, VirtualScene> {
|
||||||
|
|
||||||
private final TimeLine timeline;
|
private final TimeLine timeline;
|
||||||
|
|
||||||
|
public RenderVisitor(TimeLine timeline) {
|
||||||
|
this.timeline = timeline;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Void visit(ArcNode node, VirtualScene scene) {
|
public Void visit(ArcNode node, VirtualScene scene) {
|
||||||
return render(node, scene);
|
return render(node, scene);
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
<LineText />
|
<LineText />
|
||||||
|
|
||||||
<LineText />
|
<LineText />
|
||||||
<LineText styleClass="bold" content="0.9.1" />
|
<LineText styleClass="bold" content="1.0.0" />
|
||||||
|
|
||||||
<LineText />
|
<LineText />
|
||||||
<LineText content="Programming language for creating animations" />
|
<LineText content="Programming language for creating animations" />
|
||||||
|
@ -272,7 +272,7 @@
|
|||||||
})
|
})
|
||||||
</code>
|
</code>
|
||||||
</LibraryItem>
|
</LibraryItem>
|
||||||
<LibraryItem text="SVG Hearth">
|
<LibraryItem text="SVG Heart">
|
||||||
<graphic>
|
<graphic>
|
||||||
<SVGPath content="M23.6,0c-3.4,0-6.3,2.7-7.6,5.6C14.7,2.7,11.8,0,8.4,0C3.8,0,0,3.8,0,8.4c0,9.4,9.5,11.9,16,21.2 6.1-9.3,16-12.1,16-21.2C32,3.8,28.2,0,23.6,0z"
|
<SVGPath content="M23.6,0c-3.4,0-6.3,2.7-7.6,5.6C14.7,2.7,11.8,0,8.4,0C3.8,0,0,3.8,0,8.4c0,9.4,9.5,11.9,16,21.2 6.1-9.3,16-12.1,16-21.2C32,3.8,28.2,0,23.6,0z"
|
||||||
fill="#c91720"
|
fill="#c91720"
|
||||||
|
@ -6,7 +6,6 @@ import com.annimon.hotarufx.lib.NumberValue;
|
|||||||
import com.annimon.hotarufx.lib.Validator;
|
import com.annimon.hotarufx.lib.Validator;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import lombok.val;
|
|
||||||
import org.junit.jupiter.api.Assertions;
|
import org.junit.jupiter.api.Assertions;
|
||||||
import static com.annimon.hotarufx.bundles.FunctionInfo.of;
|
import static com.annimon.hotarufx.bundles.FunctionInfo.of;
|
||||||
import static com.annimon.hotarufx.bundles.FunctionType.COMMON;
|
import static com.annimon.hotarufx.bundles.FunctionType.COMMON;
|
||||||
@ -30,7 +29,7 @@ public class AssertionsBundle implements Bundle {
|
|||||||
private static Function assertHasVariable(Context context) {
|
private static Function assertHasVariable(Context context) {
|
||||||
return args -> {
|
return args -> {
|
||||||
Validator.with(args).check(1);
|
Validator.with(args).check(1);
|
||||||
val name = args[0].asString();
|
final var name = args[0].asString();
|
||||||
Assertions.assertTrue(context.variables().containsKey(name));
|
Assertions.assertTrue(context.variables().containsKey(name));
|
||||||
return NumberValue.ZERO;
|
return NumberValue.ZERO;
|
||||||
};
|
};
|
||||||
@ -39,7 +38,7 @@ public class AssertionsBundle implements Bundle {
|
|||||||
private static Function assertHasFunction(Context context) {
|
private static Function assertHasFunction(Context context) {
|
||||||
return args -> {
|
return args -> {
|
||||||
Validator.with(args).check(1);
|
Validator.with(args).check(1);
|
||||||
val name = args[0].asString();
|
final var name = args[0].asString();
|
||||||
Assertions.assertTrue(context.functions().containsKey(name));
|
Assertions.assertTrue(context.functions().containsKey(name));
|
||||||
return NumberValue.ZERO;
|
return NumberValue.ZERO;
|
||||||
};
|
};
|
||||||
@ -56,8 +55,8 @@ public class AssertionsBundle implements Bundle {
|
|||||||
private static Function assertEquals(Context context) {
|
private static Function assertEquals(Context context) {
|
||||||
return args -> {
|
return args -> {
|
||||||
Validator.with(args).check(2);
|
Validator.with(args).check(2);
|
||||||
val expectedValue = args[0];
|
final var expectedValue = args[0];
|
||||||
val actualValue = args[1];
|
final var actualValue = args[1];
|
||||||
Assertions.assertEquals(expectedValue, actualValue);
|
Assertions.assertEquals(expectedValue, actualValue);
|
||||||
return NumberValue.ZERO;
|
return NumberValue.ZERO;
|
||||||
};
|
};
|
||||||
|
@ -2,7 +2,6 @@ package com.annimon.hotarufx.bundles;
|
|||||||
|
|
||||||
import com.annimon.hotarufx.lib.Context;
|
import com.annimon.hotarufx.lib.Context;
|
||||||
import com.annimon.hotarufx.lib.NumberValue;
|
import com.annimon.hotarufx.lib.NumberValue;
|
||||||
import lombok.val;
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
import static org.hamcrest.Matchers.*;
|
import static org.hamcrest.Matchers.*;
|
||||||
@ -11,7 +10,7 @@ class CompositionBundleTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testBundle() {
|
void testBundle() {
|
||||||
val context = new Context();
|
final var context = new Context();
|
||||||
BundleLoader.loadSingle(context, CompositionBundle.class);
|
BundleLoader.loadSingle(context, CompositionBundle.class);
|
||||||
|
|
||||||
assertThat(context.functions(), hasKey("composition"));
|
assertThat(context.functions(), hasKey("composition"));
|
||||||
|
@ -10,7 +10,6 @@ import com.annimon.hotarufx.visual.objects.CircleNode;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import javafx.scene.paint.Color;
|
import javafx.scene.paint.Color;
|
||||||
import lombok.val;
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
import static org.hamcrest.Matchers.*;
|
import static org.hamcrest.Matchers.*;
|
||||||
@ -19,7 +18,7 @@ class NodesBundleTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testBundle() {
|
void testBundle() {
|
||||||
val context = new Context();
|
final var context = new Context();
|
||||||
BundleLoader.load(context, Arrays.asList(
|
BundleLoader.load(context, Arrays.asList(
|
||||||
CompositionBundle.class,
|
CompositionBundle.class,
|
||||||
NodesBundle.class
|
NodesBundle.class
|
||||||
@ -27,20 +26,20 @@ class NodesBundleTest {
|
|||||||
|
|
||||||
assertThat(context.functions(), hasKey("circle"));
|
assertThat(context.functions(), hasKey("circle"));
|
||||||
|
|
||||||
val map = new HashMap<String, Value>();
|
final var map = new HashMap<String, Value>();
|
||||||
map.put("translateZ", NumberValue.of(-40));
|
map.put("translateZ", NumberValue.of(-40));
|
||||||
map.put("cx", NumberValue.of(-10));
|
map.put("cx", NumberValue.of(-10));
|
||||||
map.put("radius", NumberValue.of(50));
|
map.put("radius", NumberValue.of(50));
|
||||||
map.put("fill", new StringValue("#00AA00"));
|
map.put("fill", new StringValue("#00AA00"));
|
||||||
val value = context.functions().get("circle").execute(new MapValue(map));
|
final var value = context.functions().get("circle").execute(new MapValue(map));
|
||||||
|
|
||||||
assertThat(value, instanceOf(NodeValue.class));
|
assertThat(value, instanceOf(NodeValue.class));
|
||||||
|
|
||||||
val nodeValue = (NodeValue) value;
|
final var nodeValue = (NodeValue) value;
|
||||||
assertThat(nodeValue.getNode(), notNullValue());
|
assertThat(nodeValue.getNode(), notNullValue());
|
||||||
assertThat(nodeValue.getNode(), instanceOf(CircleNode.class));
|
assertThat(nodeValue.getNode(), instanceOf(CircleNode.class));
|
||||||
|
|
||||||
val circle = (CircleNode) nodeValue.getNode();
|
final var circle = (CircleNode) nodeValue.getNode();
|
||||||
assertThat(circle.circle.getCenterX(), closeTo(-10, 0.001));
|
assertThat(circle.circle.getCenterX(), closeTo(-10, 0.001));
|
||||||
assertThat(circle.circle.getRadius(), closeTo(50, 0.001));
|
assertThat(circle.circle.getRadius(), closeTo(50, 0.001));
|
||||||
assertThat(circle.circle.getFill(), is(Color.web("#00AA00")));
|
assertThat(circle.circle.getFill(), is(Color.web("#00AA00")));
|
||||||
|
@ -5,7 +5,6 @@ import com.annimon.hotarufx.lib.Function;
|
|||||||
import com.annimon.hotarufx.lib.NumberValue;
|
import com.annimon.hotarufx.lib.NumberValue;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import lombok.val;
|
|
||||||
import static com.annimon.hotarufx.bundles.FunctionInfo.of;
|
import static com.annimon.hotarufx.bundles.FunctionInfo.of;
|
||||||
import static com.annimon.hotarufx.bundles.FunctionType.COMMON;
|
import static com.annimon.hotarufx.bundles.FunctionType.COMMON;
|
||||||
|
|
||||||
@ -46,13 +45,13 @@ public class PrintBundle implements Bundle {
|
|||||||
|
|
||||||
private static Function dump(Context context) {
|
private static Function dump(Context context) {
|
||||||
return args -> {
|
return args -> {
|
||||||
val maxVariableLength = context.variables()
|
final var maxVariableLength = context.variables()
|
||||||
.keySet().stream()
|
.keySet().stream()
|
||||||
.mapToInt(String::length)
|
.mapToInt(String::length)
|
||||||
.max()
|
.max()
|
||||||
.orElse(20);
|
.orElse(20);
|
||||||
System.out.println("\n\tVARIABLES");
|
System.out.println("\n\tVARIABLES");
|
||||||
val format = "%"+maxVariableLength+"s %s%n";
|
final var format = "%"+maxVariableLength+"s %s%n";
|
||||||
context.variables().forEach((k, v) -> {
|
context.variables().forEach((k, v) -> {
|
||||||
System.out.printf(format, k, v);
|
System.out.printf(format, k, v);
|
||||||
});
|
});
|
||||||
|
@ -14,7 +14,6 @@ import java.nio.file.Paths;
|
|||||||
import java.nio.file.StandardOpenOption;
|
import java.nio.file.StandardOpenOption;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
import lombok.val;
|
|
||||||
import org.junit.jupiter.api.DisplayName;
|
import org.junit.jupiter.api.DisplayName;
|
||||||
import org.junit.jupiter.params.ParameterizedTest;
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
import org.junit.jupiter.params.provider.Arguments;
|
import org.junit.jupiter.params.provider.Arguments;
|
||||||
@ -45,8 +44,8 @@ class ProgramTest {
|
|||||||
@MethodSource("programPathProvider")
|
@MethodSource("programPathProvider")
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
void testProgram(Path path) {
|
void testProgram(Path path) {
|
||||||
val context = new Context();
|
final var context = new Context();
|
||||||
val bundles = new ArrayList<Class<? extends Bundle>>();
|
final var bundles = new ArrayList<Class<? extends Bundle>>();
|
||||||
bundles.addAll(BundleLoader.runtimeBundles());
|
bundles.addAll(BundleLoader.runtimeBundles());
|
||||||
bundles.add(AssertionsBundle.class);
|
bundles.add(AssertionsBundle.class);
|
||||||
bundles.add(PrintBundle.class);
|
bundles.add(PrintBundle.class);
|
||||||
|
@ -12,7 +12,6 @@ import com.annimon.hotarufx.parser.ast.UnitNode;
|
|||||||
import com.annimon.hotarufx.parser.ast.ValueNode;
|
import com.annimon.hotarufx.parser.ast.ValueNode;
|
||||||
import com.annimon.hotarufx.parser.ast.VariableNode;
|
import com.annimon.hotarufx.parser.ast.VariableNode;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import lombok.val;
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
import static org.hamcrest.Matchers.allOf;
|
import static org.hamcrest.Matchers.allOf;
|
||||||
@ -69,23 +68,23 @@ class HotaruParserTest {
|
|||||||
Node node = p(input);
|
Node node = p(input);
|
||||||
assertThat(node, instanceOf(BlockNode.class));
|
assertThat(node, instanceOf(BlockNode.class));
|
||||||
|
|
||||||
val block = (BlockNode) node;
|
final var block = (BlockNode) node;
|
||||||
assertThat(block.statements.size(), is(2));
|
assertThat(block.statements.size(), is(2));
|
||||||
|
|
||||||
val expectedValues = Arrays.asList(
|
final var expectedValues = Arrays.asList(
|
||||||
NumberValue.fromBoolean(true),
|
NumberValue.fromBoolean(true),
|
||||||
NumberValue.fromBoolean(false)
|
NumberValue.fromBoolean(false)
|
||||||
);
|
);
|
||||||
val it = expectedValues.iterator();
|
final var it = expectedValues.iterator();
|
||||||
|
|
||||||
for (Node statement : block.statements) {
|
for (Node statement : block.statements) {
|
||||||
assertThat(statement, instanceOf(AssignNode.class));
|
assertThat(statement, instanceOf(AssignNode.class));
|
||||||
|
|
||||||
val assignNode = (AssignNode) statement;
|
final var assignNode = (AssignNode) statement;
|
||||||
assertThat(assignNode.target, instanceOf(VariableNode.class));
|
assertThat(assignNode.target, instanceOf(VariableNode.class));
|
||||||
assertThat(assignNode.value, instanceOf(ValueNode.class));
|
assertThat(assignNode.value, instanceOf(ValueNode.class));
|
||||||
|
|
||||||
val value = ((ValueNode) assignNode.value).value;
|
final var value = ((ValueNode) assignNode.value).value;
|
||||||
assertThat(value, is(it.next()));
|
assertThat(value, is(it.next()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -96,26 +95,26 @@ class HotaruParserTest {
|
|||||||
Node node = p(input);
|
Node node = p(input);
|
||||||
assertThat(node, instanceOf(BlockNode.class));
|
assertThat(node, instanceOf(BlockNode.class));
|
||||||
|
|
||||||
val block = (BlockNode) node;
|
final var block = (BlockNode) node;
|
||||||
assertThat(block.statements.size(), is(2));
|
assertThat(block.statements.size(), is(2));
|
||||||
|
|
||||||
val expectedValues = Arrays.asList(
|
final var expectedValues = Arrays.asList(
|
||||||
NumberValue.of(500),
|
NumberValue.of(500),
|
||||||
NumberValue.of(0.5)
|
NumberValue.of(0.5)
|
||||||
);
|
);
|
||||||
val it = expectedValues.iterator();
|
final var it = expectedValues.iterator();
|
||||||
|
|
||||||
for (Node statement : block.statements) {
|
for (Node statement : block.statements) {
|
||||||
assertThat(statement, instanceOf(AssignNode.class));
|
assertThat(statement, instanceOf(AssignNode.class));
|
||||||
|
|
||||||
val assignNode = (AssignNode) statement;
|
final var assignNode = (AssignNode) statement;
|
||||||
assertThat(assignNode.target, instanceOf(VariableNode.class));
|
assertThat(assignNode.target, instanceOf(VariableNode.class));
|
||||||
assertThat(assignNode.value, instanceOf(UnitNode.class));
|
assertThat(assignNode.value, instanceOf(UnitNode.class));
|
||||||
|
|
||||||
val unitNode = (UnitNode) assignNode.value;
|
final var unitNode = (UnitNode) assignNode.value;
|
||||||
assertThat(unitNode.value, instanceOf(ValueNode.class));
|
assertThat(unitNode.value, instanceOf(ValueNode.class));
|
||||||
|
|
||||||
val value = ((ValueNode) unitNode.value).value;
|
final var value = ((ValueNode) unitNode.value).value;
|
||||||
assertThat(value, is(it.next()));
|
assertThat(value, is(it.next()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,6 @@ import com.annimon.hotarufx.lib.Value;
|
|||||||
import com.annimon.hotarufx.parser.HotaruParser;
|
import com.annimon.hotarufx.parser.HotaruParser;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import lombok.val;
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
import static org.hamcrest.Matchers.*;
|
import static org.hamcrest.Matchers.*;
|
||||||
@ -120,7 +119,7 @@ class InterpreterVisitorTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testUnits() {
|
void testUnits() {
|
||||||
val context = new Context();
|
final var context = new Context();
|
||||||
BundleLoader.loadSingle(context, CompositionBundle.class);
|
BundleLoader.loadSingle(context, CompositionBundle.class);
|
||||||
context.functions().put("rate", context.functions().get("composition"));
|
context.functions().put("rate", context.functions().get("composition"));
|
||||||
Value value;
|
Value value;
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package com.annimon.hotarufx.visual;
|
package com.annimon.hotarufx.visual;
|
||||||
|
|
||||||
import lombok.val;
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
import static org.hamcrest.Matchers.contains;
|
import static org.hamcrest.Matchers.contains;
|
||||||
@ -9,7 +8,7 @@ class TimeLineTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void add() {
|
void add() {
|
||||||
val timeline = new PropertyTimeline<String>(null);
|
final var timeline = new PropertyTimeline<String>(null);
|
||||||
timeline.add(KeyFrame.of(20), null);
|
timeline.add(KeyFrame.of(20), null);
|
||||||
timeline.add(KeyFrame.of(10), null);
|
timeline.add(KeyFrame.of(10), null);
|
||||||
timeline.add(KeyFrame.of(0), null);
|
timeline.add(KeyFrame.of(0), null);
|
||||||
|
@ -10,7 +10,6 @@ import javafx.scene.paint.Color;
|
|||||||
import javafx.scene.paint.Paint;
|
import javafx.scene.paint.Paint;
|
||||||
import javafx.scene.text.Font;
|
import javafx.scene.text.Font;
|
||||||
import javafx.scene.text.Text;
|
import javafx.scene.text.Text;
|
||||||
import lombok.val;
|
|
||||||
import org.junit.jupiter.api.DisplayName;
|
import org.junit.jupiter.api.DisplayName;
|
||||||
import org.junit.jupiter.params.ParameterizedTest;
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
import org.junit.jupiter.params.provider.Arguments;
|
import org.junit.jupiter.params.provider.Arguments;
|
||||||
@ -52,36 +51,36 @@ class NodePropertiesTypeTest {
|
|||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
void testNode(ObjectNode node, String name, Property property, String nodeName) {
|
void testNode(ObjectNode node, String name, Property property, String nodeName) {
|
||||||
try {
|
try {
|
||||||
val value = property.getProperty().get().getProperty();
|
final var value = property.getProperty().get().getProperty();
|
||||||
switch (property.getType()) {
|
switch (property.getType()) {
|
||||||
case BOOLEAN:
|
case BOOLEAN:
|
||||||
val booleanValue = (WritableValue<Boolean>) value;
|
final var booleanValue = (WritableValue<Boolean>) value;
|
||||||
booleanValue.setValue(true);
|
booleanValue.setValue(true);
|
||||||
assertTrue(booleanValue.getValue());
|
assertTrue(booleanValue.getValue());
|
||||||
break;
|
break;
|
||||||
case NUMBER:
|
case NUMBER:
|
||||||
val numberValue = (WritableValue<Number>) value;
|
final var numberValue = (WritableValue<Number>) value;
|
||||||
numberValue.setValue(2);
|
numberValue.setValue(2);
|
||||||
assertThat(numberValue.getValue().intValue(), is(2));
|
assertThat(numberValue.getValue().intValue(), is(2));
|
||||||
break;
|
break;
|
||||||
case STRING:
|
case STRING:
|
||||||
val stringValue = (WritableValue<String>) value;
|
final var stringValue = (WritableValue<String>) value;
|
||||||
stringValue.setValue("0");
|
stringValue.setValue("0");
|
||||||
assertThat(stringValue.getValue(), is("0"));
|
assertThat(stringValue.getValue(), is("0"));
|
||||||
break;
|
break;
|
||||||
case NODE:
|
case NODE:
|
||||||
case CLIP_NODE:
|
case CLIP_NODE:
|
||||||
val nodeValue = (WritableValue<Node>) value;
|
final var nodeValue = (WritableValue<Node>) value;
|
||||||
nodeValue.setValue(new Text("test"));
|
nodeValue.setValue(new Text("test"));
|
||||||
assertThat(((Text) nodeValue.getValue()).getText(), is("test"));
|
assertThat(((Text) nodeValue.getValue()).getText(), is("test"));
|
||||||
break;
|
break;
|
||||||
case PAINT:
|
case PAINT:
|
||||||
val paintValue = (WritableValue<Paint>) value;
|
final var paintValue = (WritableValue<Paint>) value;
|
||||||
paintValue.setValue(Color.BLUE);
|
paintValue.setValue(Color.BLUE);
|
||||||
assertThat(paintValue.getValue(), is(Color.BLUE));
|
assertThat(paintValue.getValue(), is(Color.BLUE));
|
||||||
break;
|
break;
|
||||||
case FONT:
|
case FONT:
|
||||||
val fontValue = (WritableValue<Font>) value;
|
final var fontValue = (WritableValue<Font>) value;
|
||||||
fontValue.setValue(Font.getDefault());
|
fontValue.setValue(Font.getDefault());
|
||||||
assertThat(fontValue.getValue().getFamily(), is(Font.getDefault().getFamily()));
|
assertThat(fontValue.getValue().getFamily(), is(Font.getDefault().getFamily()));
|
||||||
break;
|
break;
|
||||||
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
3
gradle/wrapper/gradle-wrapper.properties
vendored
3
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,6 +1,5 @@
|
|||||||
#Mon Aug 21 12:17:04 EEST 2017
|
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.3-bin.zip
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-bin.zip
|
|
||||||
|
28
gradlew
vendored
28
gradlew
vendored
@ -1,5 +1,21 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
#
|
||||||
|
# Copyright 2015 the original author or authors.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
#
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
##
|
##
|
||||||
## Gradle start up script for UN*X
|
## Gradle start up script for UN*X
|
||||||
@ -28,16 +44,16 @@ APP_NAME="Gradle"
|
|||||||
APP_BASE_NAME=`basename "$0"`
|
APP_BASE_NAME=`basename "$0"`
|
||||||
|
|
||||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
DEFAULT_JVM_OPTS=""
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
|
|
||||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
MAX_FD="maximum"
|
MAX_FD="maximum"
|
||||||
|
|
||||||
warn ( ) {
|
warn () {
|
||||||
echo "$*"
|
echo "$*"
|
||||||
}
|
}
|
||||||
|
|
||||||
die ( ) {
|
die () {
|
||||||
echo
|
echo
|
||||||
echo "$*"
|
echo "$*"
|
||||||
echo
|
echo
|
||||||
@ -109,8 +125,8 @@ if $darwin; then
|
|||||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# For Cygwin, switch paths to Windows format before running java
|
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||||
if $cygwin ; then
|
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||||
@ -155,7 +171,7 @@ if $cygwin ; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Escape application args
|
# Escape application args
|
||||||
save ( ) {
|
save () {
|
||||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||||
echo " "
|
echo " "
|
||||||
}
|
}
|
||||||
|
18
gradlew.bat
vendored
18
gradlew.bat
vendored
@ -1,3 +1,19 @@
|
|||||||
|
@rem
|
||||||
|
@rem Copyright 2015 the original author or authors.
|
||||||
|
@rem
|
||||||
|
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
@rem you may not use this file except in compliance with the License.
|
||||||
|
@rem You may obtain a copy of the License at
|
||||||
|
@rem
|
||||||
|
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
@rem
|
||||||
|
@rem Unless required by applicable law or agreed to in writing, software
|
||||||
|
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
@rem See the License for the specific language governing permissions and
|
||||||
|
@rem limitations under the License.
|
||||||
|
@rem
|
||||||
|
|
||||||
@if "%DEBUG%" == "" @echo off
|
@if "%DEBUG%" == "" @echo off
|
||||||
@rem ##########################################################################
|
@rem ##########################################################################
|
||||||
@rem
|
@rem
|
||||||
@ -14,7 +30,7 @@ set APP_BASE_NAME=%~n0
|
|||||||
set APP_HOME=%DIRNAME%
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
set DEFAULT_JVM_OPTS=
|
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||||
|
|
||||||
@rem Find java.exe
|
@rem Find java.exe
|
||||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||||
|
Loading…
Reference in New Issue
Block a user