mirror of
https://github.com/aNNiMON/HotaruFX.git
synced 2024-09-19 14:14:21 +03:00
Add map support
This commit is contained in:
parent
d13c8ee849
commit
2a8d336fff
@ -0,0 +1,8 @@
|
||||
package com.annimon.hotarufx.exceptions;
|
||||
|
||||
public class TypeException extends HotaruRuntimeException {
|
||||
|
||||
public TypeException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
98
app/src/main/java/com/annimon/hotarufx/lib/MapValue.java
Normal file
98
app/src/main/java/com/annimon/hotarufx/lib/MapValue.java
Normal file
@ -0,0 +1,98 @@
|
||||
package com.annimon.hotarufx.lib;
|
||||
|
||||
import com.annimon.hotarufx.exceptions.TypeException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
public class MapValue implements Value, Iterable<Map.Entry<String, Value>> {
|
||||
|
||||
public static final MapValue EMPTY = new MapValue(1);
|
||||
|
||||
public static MapValue merge(MapValue map1, MapValue map2) {
|
||||
final MapValue result = new MapValue(map1.size() + map2.size());
|
||||
result.map.putAll(map1.map);
|
||||
result.map.putAll(map2.map);
|
||||
return result;
|
||||
}
|
||||
|
||||
private final Map<String, Value> map;
|
||||
|
||||
public MapValue(int size) {
|
||||
this.map = new HashMap<>(size);
|
||||
}
|
||||
|
||||
public MapValue(Map<String, Value> map) {
|
||||
this.map = map;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int type() {
|
||||
return Types.MAP;
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return map.size();
|
||||
}
|
||||
|
||||
public Map<String, Value> getMap() {
|
||||
return map;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object raw() {
|
||||
return map;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int asInt() {
|
||||
throw new TypeException("Cannot cast map to integer");
|
||||
}
|
||||
|
||||
@Override
|
||||
public double asNumber() {
|
||||
throw new TypeException("Cannot cast map to number");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String asString() {
|
||||
return map.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Map.Entry<String, Value>> iterator() {
|
||||
return map.entrySet().iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 5;
|
||||
hash = 37 * hash + Objects.hashCode(this.map);
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) return true;
|
||||
if (obj == null) return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
final MapValue other = (MapValue) obj;
|
||||
return Objects.equals(this.map, other.map);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Value o) {
|
||||
if (o.type() == Types.MAP) {
|
||||
final int lengthCompare = Integer.compare(size(), ((MapValue) o).size());
|
||||
if (lengthCompare != 0) return lengthCompare;
|
||||
}
|
||||
return asString().compareTo(o.asString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return asString();
|
||||
}
|
||||
}
|
@ -2,11 +2,14 @@ package com.annimon.hotarufx.parser.visitors;
|
||||
|
||||
import com.annimon.hotarufx.exceptions.VariableNotFoundException;
|
||||
import com.annimon.hotarufx.lib.Context;
|
||||
import com.annimon.hotarufx.lib.MapValue;
|
||||
import com.annimon.hotarufx.lib.NumberValue;
|
||||
import com.annimon.hotarufx.lib.StringValue;
|
||||
import com.annimon.hotarufx.lib.Types;
|
||||
import com.annimon.hotarufx.lib.Value;
|
||||
import com.annimon.hotarufx.parser.ast.*;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import lombok.val;
|
||||
|
||||
public class InterpreterVisitor implements ResultVisitor<Value, Context> {
|
||||
@ -38,7 +41,11 @@ public class InterpreterVisitor implements ResultVisitor<Value, Context> {
|
||||
|
||||
@Override
|
||||
public Value visit(MapNode node, Context context) {
|
||||
return NumberValue.ZERO;
|
||||
Map<String, Value> map = new HashMap<>(node.elements.size());
|
||||
for (Map.Entry<String, Node> entry : node.elements.entrySet()) {
|
||||
map.put(entry.getKey(), entry.getValue().accept(this, context));
|
||||
}
|
||||
return new MapValue(map);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -3,13 +3,17 @@ package com.annimon.hotarufx.parser.visitors;
|
||||
import com.annimon.hotarufx.exceptions.VariableNotFoundException;
|
||||
import com.annimon.hotarufx.lexer.HotaruLexer;
|
||||
import com.annimon.hotarufx.lib.Context;
|
||||
import com.annimon.hotarufx.lib.MapValue;
|
||||
import com.annimon.hotarufx.lib.NumberValue;
|
||||
import com.annimon.hotarufx.lib.StringValue;
|
||||
import com.annimon.hotarufx.lib.Value;
|
||||
import com.annimon.hotarufx.parser.HotaruParser;
|
||||
import java.util.Map;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.allOf;
|
||||
import static org.hamcrest.Matchers.hasEntry;
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
@ -44,6 +48,19 @@ class InterpreterVisitorTest {
|
||||
assertThat(eval("A = -1").asInt(), is(-1));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMap() {
|
||||
Value value = eval("A = {x: 0, y: 1, text: 'hello'}");
|
||||
assertThat(value, instanceOf(MapValue.class));
|
||||
|
||||
Map<String, Value> map = ((MapValue) value).getMap();
|
||||
assertThat(map, allOf(
|
||||
hasEntry("x", NumberValue.of(0)),
|
||||
hasEntry("y", NumberValue.of(1))
|
||||
));
|
||||
assertThat(map, hasEntry("text", new StringValue("hello")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRuntimeErrors() {
|
||||
assertThrows(VariableNotFoundException.class, () -> eval("A = B"));
|
||||
|
Loading…
Reference in New Issue
Block a user