diff --git a/app/src/main/java/com/annimon/hotarufx/lib/NodeValue.java b/app/src/main/java/com/annimon/hotarufx/lib/NodeValue.java index c647371..f8d98e1 100644 --- a/app/src/main/java/com/annimon/hotarufx/lib/NodeValue.java +++ b/app/src/main/java/com/annimon/hotarufx/lib/NodeValue.java @@ -1,5 +1,6 @@ package com.annimon.hotarufx.lib; +import com.annimon.hotarufx.exceptions.HotaruRuntimeException; import com.annimon.hotarufx.exceptions.TypeException; import com.annimon.hotarufx.visual.Property; import com.annimon.hotarufx.visual.PropertyBindings; @@ -32,24 +33,46 @@ public class NodeValue implements Value { return node; } - @SuppressWarnings("unchecked") public void fill(MapValue map) { - map.getMap().forEach((key, value) -> { - if (!bindings.containsKey(key)) return; - final Property property = bindings.get(key); - val timeline = property.getProperty().get(); - val type = property.getType(); - switch (type) { - case NUMBER: - ((PropertyTimeline) timeline).getProperty().setValue( - type.getFromHFX().apply(value)); - break; - case PAINT: - ((PropertyTimeline) timeline).getProperty().setValue( - type.getFromHFX().apply(value)); - break; - } - }); + map.getMap().forEach(this::set); + } + + @SuppressWarnings("unchecked") + public Value get(String key) { + if (!bindings.containsKey(key)) { + throw new HotaruRuntimeException("Unable to get property " + key + " from node value"); + } + final Property property = bindings.get(key); + val timeline = property.getProperty().get(); + val type = property.getType(); + switch (type) { + case NUMBER: + return type.getToHFX().apply( + ((PropertyTimeline) timeline).getProperty().getValue()); + case PAINT: + return type.getToHFX().apply( + ((PropertyTimeline) timeline).getProperty().getValue()); + default: + throw new TypeException("Unknown type of node property"); + } + } + + @SuppressWarnings("unchecked") + public void set(String key, Value value) { + if (!bindings.containsKey(key)) return; + final Property property = bindings.get(key); + val timeline = property.getProperty().get(); + val type = property.getType(); + switch (type) { + case NUMBER: + ((PropertyTimeline) timeline).getProperty().setValue( + type.getFromHFX().apply(value)); + break; + case PAINT: + ((PropertyTimeline) timeline).getProperty().setValue( + type.getFromHFX().apply(value)); + break; + } } @Override diff --git a/app/src/main/java/com/annimon/hotarufx/parser/visitors/InterpreterVisitor.java b/app/src/main/java/com/annimon/hotarufx/parser/visitors/InterpreterVisitor.java index b37008d..7bad326 100644 --- a/app/src/main/java/com/annimon/hotarufx/parser/visitors/InterpreterVisitor.java +++ b/app/src/main/java/com/annimon/hotarufx/parser/visitors/InterpreterVisitor.java @@ -5,6 +5,7 @@ import com.annimon.hotarufx.exceptions.TypeException; import com.annimon.hotarufx.exceptions.VariableNotFoundException; import com.annimon.hotarufx.lib.Context; import com.annimon.hotarufx.lib.MapValue; +import com.annimon.hotarufx.lib.NodeValue; import com.annimon.hotarufx.lib.NumberValue; import com.annimon.hotarufx.lib.StringValue; import com.annimon.hotarufx.lib.Types; @@ -102,7 +103,7 @@ public class InterpreterVisitor implements ResultVisitor { public Value get(AccessNode node, Context context) { Value container = node.root.accept(this, context); return getContainer(node.indices, context, container) - .orElseThrow(() -> new TypeException("Map expected", node.start(), node.end())); + .orElseThrow(() -> new TypeException("Unable to get property from non-accessible type", node.start(), node.end())); } @Override @@ -110,14 +111,20 @@ public class InterpreterVisitor implements ResultVisitor { Value container = node.root.accept(this, context); int lastIndex = node.indices.size() - 1; Supplier exceptionSupplier = () -> - new TypeException("Map expected", node.start(), node.end()); + new TypeException("Unable to set property to non-accessible type", node.start(), node.end()); container = getContainer(node.indices.subList(0, lastIndex), context, container) .orElseThrow(exceptionSupplier); switch (container.type()) { - case Types.MAP: + case Types.MAP: { val key = node.indices.get(lastIndex).accept(this, context).asString(); ((MapValue) container).getMap().put(key, value); - break; + } break; + + case Types.NODE: { + val key = node.indices.get(lastIndex).accept(this, context).asString(); + ((NodeValue) container).set(key, value); + } break; + default: throw exceptionSupplier.get(); } @@ -127,10 +134,16 @@ public class InterpreterVisitor implements ResultVisitor { private Optional getContainer(List nodes, Context context, Value container) { for (Node index : nodes) { switch (container.type()) { - case Types.MAP: + case Types.MAP: { val key = index.accept(this, context).asString(); container = ((MapValue) container).getMap().get(key); - break; + } break; + + case Types.NODE: { + val key = index.accept(this, context).asString(); + container = ((NodeValue) container).get(key); + } break; + default: return Optional.empty(); } diff --git a/app/src/main/resources/main.hfx b/app/src/main/resources/main.hfx index 0cbdd5c..6d4c1ed 100644 --- a/app/src/main/resources/main.hfx +++ b/app/src/main/resources/main.hfx @@ -7,4 +7,6 @@ A = circle({ fill: "red" }) +A.radius = 10 + render(A) \ No newline at end of file