1
0
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:
Victor 2017-08-23 18:25:34 +03:00
parent d13c8ee849
commit 2a8d336fff
4 changed files with 131 additions and 1 deletions

View File

@ -0,0 +1,8 @@
package com.annimon.hotarufx.exceptions;
public class TypeException extends HotaruRuntimeException {
public TypeException(String message) {
super(message);
}
}

View 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();
}
}

View File

@ -2,11 +2,14 @@ package com.annimon.hotarufx.parser.visitors;
import com.annimon.hotarufx.exceptions.VariableNotFoundException; import com.annimon.hotarufx.exceptions.VariableNotFoundException;
import com.annimon.hotarufx.lib.Context; import com.annimon.hotarufx.lib.Context;
import com.annimon.hotarufx.lib.MapValue;
import com.annimon.hotarufx.lib.NumberValue; import com.annimon.hotarufx.lib.NumberValue;
import com.annimon.hotarufx.lib.StringValue; import com.annimon.hotarufx.lib.StringValue;
import com.annimon.hotarufx.lib.Types; import com.annimon.hotarufx.lib.Types;
import com.annimon.hotarufx.lib.Value; import com.annimon.hotarufx.lib.Value;
import com.annimon.hotarufx.parser.ast.*; import com.annimon.hotarufx.parser.ast.*;
import java.util.HashMap;
import java.util.Map;
import lombok.val; import lombok.val;
public class InterpreterVisitor implements ResultVisitor<Value, Context> { public class InterpreterVisitor implements ResultVisitor<Value, Context> {
@ -38,7 +41,11 @@ public class InterpreterVisitor implements ResultVisitor<Value, Context> {
@Override @Override
public Value visit(MapNode node, Context context) { 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 @Override

View File

@ -3,13 +3,17 @@ package com.annimon.hotarufx.parser.visitors;
import com.annimon.hotarufx.exceptions.VariableNotFoundException; import com.annimon.hotarufx.exceptions.VariableNotFoundException;
import com.annimon.hotarufx.lexer.HotaruLexer; import com.annimon.hotarufx.lexer.HotaruLexer;
import com.annimon.hotarufx.lib.Context; import com.annimon.hotarufx.lib.Context;
import com.annimon.hotarufx.lib.MapValue;
import com.annimon.hotarufx.lib.NumberValue; import com.annimon.hotarufx.lib.NumberValue;
import com.annimon.hotarufx.lib.StringValue;
import com.annimon.hotarufx.lib.Value; import com.annimon.hotarufx.lib.Value;
import com.annimon.hotarufx.parser.HotaruParser; import com.annimon.hotarufx.parser.HotaruParser;
import java.util.Map;
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;
import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.hasEntry;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.is;
import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertThrows;
@ -44,6 +48,19 @@ class InterpreterVisitorTest {
assertThat(eval("A = -1").asInt(), is(-1)); 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 @Test
void testRuntimeErrors() { void testRuntimeErrors() {
assertThrows(VariableNotFoundException.class, () -> eval("A = B")); assertThrows(VariableNotFoundException.class, () -> eval("A = B"));