diff --git a/projects/make.bsd/wren_test.make b/projects/make.bsd/wren_test.make
index 504c713e..6d8c6383 100644
--- a/projects/make.bsd/wren_test.make
+++ b/projects/make.bsd/wren_test.make
@@ -121,6 +121,7 @@ OBJECTS += $(OBJDIR)/get_variable.o
OBJECTS += $(OBJDIR)/handle.o
OBJECTS += $(OBJDIR)/lists.o
OBJECTS += $(OBJDIR)/main.o
+OBJECTS += $(OBJDIR)/maps.o
OBJECTS += $(OBJDIR)/new_vm.o
OBJECTS += $(OBJDIR)/reset_stack_after_call_abort.o
OBJECTS += $(OBJDIR)/reset_stack_after_foreign_construct.o
@@ -219,6 +220,9 @@ $(OBJDIR)/handle.o: ../../test/api/handle.c
$(OBJDIR)/lists.o: ../../test/api/lists.c
@echo $(notdir $<)
$(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"
+$(OBJDIR)/maps.o: ../../test/api/maps.c
+ @echo $(notdir $<)
+ $(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"
$(OBJDIR)/new_vm.o: ../../test/api/new_vm.c
@echo $(notdir $<)
$(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"
diff --git a/projects/make.mac/wren_test.make b/projects/make.mac/wren_test.make
index 549bee52..cbf105b9 100644
--- a/projects/make.mac/wren_test.make
+++ b/projects/make.mac/wren_test.make
@@ -129,6 +129,7 @@ OBJECTS += $(OBJDIR)/get_variable.o
OBJECTS += $(OBJDIR)/handle.o
OBJECTS += $(OBJDIR)/lists.o
OBJECTS += $(OBJDIR)/main.o
+OBJECTS += $(OBJDIR)/maps.o
OBJECTS += $(OBJDIR)/new_vm.o
OBJECTS += $(OBJDIR)/reset_stack_after_call_abort.o
OBJECTS += $(OBJDIR)/reset_stack_after_foreign_construct.o
@@ -227,6 +228,9 @@ $(OBJDIR)/handle.o: ../../test/api/handle.c
$(OBJDIR)/lists.o: ../../test/api/lists.c
@echo $(notdir $<)
$(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"
+$(OBJDIR)/maps.o: ../../test/api/maps.c
+ @echo $(notdir $<)
+ $(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"
$(OBJDIR)/new_vm.o: ../../test/api/new_vm.c
@echo $(notdir $<)
$(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"
diff --git a/projects/make/wren_test.make b/projects/make/wren_test.make
index 504c713e..6d8c6383 100644
--- a/projects/make/wren_test.make
+++ b/projects/make/wren_test.make
@@ -121,6 +121,7 @@ OBJECTS += $(OBJDIR)/get_variable.o
OBJECTS += $(OBJDIR)/handle.o
OBJECTS += $(OBJDIR)/lists.o
OBJECTS += $(OBJDIR)/main.o
+OBJECTS += $(OBJDIR)/maps.o
OBJECTS += $(OBJDIR)/new_vm.o
OBJECTS += $(OBJDIR)/reset_stack_after_call_abort.o
OBJECTS += $(OBJDIR)/reset_stack_after_foreign_construct.o
@@ -219,6 +220,9 @@ $(OBJDIR)/handle.o: ../../test/api/handle.c
$(OBJDIR)/lists.o: ../../test/api/lists.c
@echo $(notdir $<)
$(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"
+$(OBJDIR)/maps.o: ../../test/api/maps.c
+ @echo $(notdir $<)
+ $(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"
$(OBJDIR)/new_vm.o: ../../test/api/new_vm.c
@echo $(notdir $<)
$(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"
diff --git a/projects/premake/premake5.exe b/projects/premake/premake5.exe
new file mode 100644
index 00000000..9048d51e
Binary files /dev/null and b/projects/premake/premake5.exe differ
diff --git a/projects/vs2017/wren_test.vcxproj b/projects/vs2017/wren_test.vcxproj
index a6ce29f0..59ed29a1 100644
--- a/projects/vs2017/wren_test.vcxproj
+++ b/projects/vs2017/wren_test.vcxproj
@@ -265,6 +265,7 @@
+
@@ -284,6 +285,7 @@
+
diff --git a/projects/vs2017/wren_test.vcxproj.filters b/projects/vs2017/wren_test.vcxproj.filters
index 82a11a0c..227465a3 100644
--- a/projects/vs2017/wren_test.vcxproj.filters
+++ b/projects/vs2017/wren_test.vcxproj.filters
@@ -36,6 +36,9 @@
api
+
+ api
+
api
@@ -87,6 +90,9 @@
api
+
+ api
+
api
diff --git a/projects/vs2019/wren_test.vcxproj b/projects/vs2019/wren_test.vcxproj
index c4e43439..7d56e6aa 100644
--- a/projects/vs2019/wren_test.vcxproj
+++ b/projects/vs2019/wren_test.vcxproj
@@ -265,6 +265,7 @@
+
@@ -284,6 +285,7 @@
+
diff --git a/projects/vs2019/wren_test.vcxproj.filters b/projects/vs2019/wren_test.vcxproj.filters
index 82a11a0c..227465a3 100644
--- a/projects/vs2019/wren_test.vcxproj.filters
+++ b/projects/vs2019/wren_test.vcxproj.filters
@@ -36,6 +36,9 @@
api
+
+ api
+
api
@@ -87,6 +90,9 @@
api
+
+ api
+
api
diff --git a/projects/xcode/wren_test.xcodeproj/project.pbxproj b/projects/xcode/wren_test.xcodeproj/project.pbxproj
index b5113b65..9d05079e 100644
--- a/projects/xcode/wren_test.xcodeproj/project.pbxproj
+++ b/projects/xcode/wren_test.xcodeproj/project.pbxproj
@@ -14,6 +14,7 @@
31D07E222B367F941ED1DC62 /* test.c in Sources */ = {isa = PBXBuildFile; fileRef = 7B2F762A1E7AFF5C394BCC6A /* test.c */; };
47E53E839E72A8F576EBBCC3 /* call.c in Sources */ = {isa = PBXBuildFile; fileRef = 2F776B0B32E83D3DB454E14B /* call.c */; };
4D8AE463A8CC37D57C2682A3 /* handle.c in Sources */ = {isa = PBXBuildFile; fileRef = 069BFCEB1DAE981D0DCA932B /* handle.c */; };
+ 4EA04F0DA52DB97F7DA6CD4D /* maps.c in Sources */ = {isa = PBXBuildFile; fileRef = 3AEABBF53E5B8E27BFC83235 /* maps.c */; };
59615B339DB000A5DFD73973 /* resolution.c in Sources */ = {isa = PBXBuildFile; fileRef = 535262BB751E0FEDB1D338FB /* resolution.c */; };
78667B8C71CC7CFE6567D9CC /* main.c in Sources */ = {isa = PBXBuildFile; fileRef = 6E0F7DF4115B07262C2BD434 /* main.c */; };
7CCD7163EDF01255A3B6BFA3 /* api_tests.c in Sources */ = {isa = PBXBuildFile; fileRef = 21B810EB49F2C99D318E572B /* api_tests.c */; };
@@ -58,6 +59,7 @@
30E3DEE9D44B0C9BEB80C529 /* reset_stack_after_foreign_construct.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = reset_stack_after_foreign_construct.h; path = ../../test/api/reset_stack_after_foreign_construct.h; sourceTree = ""; };
310165BFD4689371EB9E4BFF /* reset_stack_after_foreign_construct.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = reset_stack_after_foreign_construct.c; path = ../../test/api/reset_stack_after_foreign_construct.c; sourceTree = ""; };
33526D1DD64093CF6566735D /* slots.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = slots.c; path = ../../test/api/slots.c; sourceTree = ""; };
+ 3AEABBF53E5B8E27BFC83235 /* maps.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = maps.c; path = ../../test/api/maps.c; sourceTree = ""; };
3E23E7FBE1120EAD7037EE3B /* lists.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = lists.h; path = ../../test/api/lists.h; sourceTree = ""; };
41058591E3F3AC4373198BD1 /* lists.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = lists.c; path = ../../test/api/lists.c; sourceTree = ""; };
50338489786E3D3B6009CAC9 /* benchmark.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = benchmark.c; path = ../../test/api/benchmark.c; sourceTree = ""; };
@@ -73,6 +75,7 @@
9C375BF5B349F727A365F235 /* handle.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = handle.h; path = ../../test/api/handle.h; sourceTree = ""; };
A415E4D5A786B70728F35B15 /* call.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = call.h; path = ../../test/api/call.h; sourceTree = ""; };
ABEF15744F3A9EA66A0B6BB4 /* test.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = test.h; path = ../../test/test.h; sourceTree = ""; };
+ AF8935BFB2FA07F13466ABFF /* maps.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = maps.h; path = ../../test/api/maps.h; sourceTree = ""; };
B0175F83D8521835BFEDA5C3 /* user_data.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = user_data.c; path = ../../test/api/user_data.c; sourceTree = ""; };
B0267C45D1F229770EA75285 /* resolution.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = resolution.h; path = ../../test/api/resolution.h; sourceTree = ""; };
B1B12E89FA506CBB1D7C24C9 /* reset_stack_after_call_abort.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = reset_stack_after_call_abort.c; path = ../../test/api/reset_stack_after_call_abort.c; sourceTree = ""; };
@@ -128,6 +131,8 @@
9C375BF5B349F727A365F235 /* handle.h */,
41058591E3F3AC4373198BD1 /* lists.c */,
3E23E7FBE1120EAD7037EE3B /* lists.h */,
+ 3AEABBF53E5B8E27BFC83235 /* maps.c */,
+ AF8935BFB2FA07F13466ABFF /* maps.h */,
C22EBF6BD9415A9DC95D55AB /* new_vm.c */,
57CA1E756EDCB9A75EF8B4B5 /* new_vm.h */,
B1B12E89FA506CBB1D7C24C9 /* reset_stack_after_call_abort.c */,
@@ -259,6 +264,7 @@
E21864B54F40732750D362F5 /* get_variable.c in Sources */,
4D8AE463A8CC37D57C2682A3 /* handle.c in Sources */,
1C5491694BE6605B26F39FA9 /* lists.c in Sources */,
+ 4EA04F0DA52DB97F7DA6CD4D /* maps.c in Sources */,
29C70EE3850862555862AD23 /* new_vm.c in Sources */,
BD0CC4E181C21B533630C321 /* reset_stack_after_call_abort.c in Sources */,
8F510F77A173C5697A74FDB7 /* reset_stack_after_foreign_construct.c in Sources */,
diff --git a/src/include/wren.h b/src/include/wren.h
index 7c67fe10..264488af 100644
--- a/src/include/wren.h
+++ b/src/include/wren.h
@@ -263,6 +263,7 @@ typedef enum
WREN_TYPE_NUM,
WREN_TYPE_FOREIGN,
WREN_TYPE_LIST,
+ WREN_TYPE_MAP,
WREN_TYPE_NULL,
WREN_TYPE_STRING,
@@ -440,6 +441,9 @@ void* wrenSetSlotNewForeign(WrenVM* vm, int slot, int classSlot, size_t size);
// Stores a new empty list in [slot].
void wrenSetSlotNewList(WrenVM* vm, int slot);
+// Stores a new empty map in [slot].
+void wrenSetSlotNewMap(WrenVM* vm, int slot);
+
// Stores null in [slot].
void wrenSetSlotNull(WrenVM* vm, int slot);
@@ -470,6 +474,26 @@ void wrenGetListElement(WrenVM* vm, int listSlot, int index, int elementSlot);
// an element, use `-1` for the index.
void wrenInsertInList(WrenVM* vm, int listSlot, int index, int elementSlot);
+// Returns the number of entries in the map stored in [slot].
+int wrenGetMapCount(WrenVM* vm, int slot);
+
+// Returns true if the key in [keySlot] is found in the map placed in [mapSlot].
+bool wrenGetMapContainsKey(WrenVM* vm, int mapSlot, int keySlot);
+
+// Retrieves a value with the key in [keySlot] from the map in [mapSlot] and
+// stores it in [valueSlot].
+void wrenGetMapValue(WrenVM* vm, int mapSlot, int keySlot, int valueSlot);
+
+// Takes the value stored at [valueSlot] and inserts it into the map stored
+// at [mapSlot] with key [keySlot].
+void wrenSetMapValue(WrenVM* vm, int mapSlot, int keySlot, int valueSlot);
+
+// Removes a value from the map in [mapSlot], with the key from [keySlot],
+// and place it in [removedValueSlot]. If not found, [removedValueSlot] is
+// set to null, the same behaviour as the Wren Map API.
+void wrenRemoveMapValue(WrenVM* vm, int mapSlot, int keySlot,
+ int removedValueSlot);
+
// Looks up the top level variable with [name] in resolved [module] and stores
// it in [slot].
void wrenGetVariable(WrenVM* vm, const char* module, const char* name,
diff --git a/src/vm/wren_value.h b/src/vm/wren_value.h
index d462760b..13f69951 100644
--- a/src/vm/wren_value.h
+++ b/src/vm/wren_value.h
@@ -76,6 +76,7 @@
#define IS_FOREIGN(value) (wrenIsObjType(value, OBJ_FOREIGN)) // ObjForeign
#define IS_INSTANCE(value) (wrenIsObjType(value, OBJ_INSTANCE)) // ObjInstance
#define IS_LIST(value) (wrenIsObjType(value, OBJ_LIST)) // ObjList
+#define IS_MAP(value) (wrenIsObjType(value, OBJ_MAP)) // ObjMap
#define IS_RANGE(value) (wrenIsObjType(value, OBJ_RANGE)) // ObjRange
#define IS_STRING(value) (wrenIsObjType(value, OBJ_STRING)) // ObjString
diff --git a/src/vm/wren_vm.c b/src/vm/wren_vm.c
index 10ba8a04..194149c5 100644
--- a/src/vm/wren_vm.c
+++ b/src/vm/wren_vm.c
@@ -6,6 +6,7 @@
#include "wren_compiler.h"
#include "wren_core.h"
#include "wren_debug.h"
+#include "wren_primitive.h"
#include "wren_vm.h"
#if WREN_OPT_META
@@ -1596,6 +1597,7 @@ WrenType wrenGetSlotType(WrenVM* vm, int slot)
if (IS_NUM(vm->apiStack[slot])) return WREN_TYPE_NUM;
if (IS_FOREIGN(vm->apiStack[slot])) return WREN_TYPE_FOREIGN;
if (IS_LIST(vm->apiStack[slot])) return WREN_TYPE_LIST;
+ if (IS_MAP(vm->apiStack[slot])) return WREN_TYPE_MAP;
if (IS_NULL(vm->apiStack[slot])) return WREN_TYPE_NULL;
if (IS_STRING(vm->apiStack[slot])) return WREN_TYPE_STRING;
@@ -1694,6 +1696,11 @@ void wrenSetSlotNewList(WrenVM* vm, int slot)
setSlot(vm, slot, OBJ_VAL(wrenNewList(vm, 0)));
}
+void wrenSetSlotNewMap(WrenVM* vm, int slot)
+{
+ setSlot(vm, slot, OBJ_VAL(wrenNewMap(vm)));
+}
+
void wrenSetSlotNull(WrenVM* vm, int slot)
{
setSlot(vm, slot, NULL_VAL);
@@ -1748,6 +1755,81 @@ void wrenInsertInList(WrenVM* vm, int listSlot, int index, int elementSlot)
wrenListInsert(vm, list, vm->apiStack[elementSlot], index);
}
+int wrenGetMapCount(WrenVM* vm, int slot)
+{
+ validateApiSlot(vm, slot);
+ ASSERT(IS_MAP(vm->apiStack[slot]), "Slot must hold a map.");
+
+ ObjMap* map = AS_MAP(vm->apiStack[slot]);
+ return map->count;
+}
+
+bool wrenGetMapContainsKey(WrenVM* vm, int mapSlot, int keySlot)
+{
+ validateApiSlot(vm, mapSlot);
+ validateApiSlot(vm, keySlot);
+ ASSERT(IS_MAP(vm->apiStack[mapSlot]), "Slot must hold a map.");
+
+ Value key = vm->apiStack[keySlot];
+ if (!validateKey(vm, key)) return false;
+
+ ObjMap* map = AS_MAP(vm->apiStack[mapSlot]);
+ Value value = wrenMapGet(map, key);
+
+ return !IS_UNDEFINED(value);
+}
+
+void wrenGetMapValue(WrenVM* vm, int mapSlot, int keySlot, int valueSlot)
+{
+ validateApiSlot(vm, mapSlot);
+ validateApiSlot(vm, keySlot);
+ validateApiSlot(vm, valueSlot);
+ ASSERT(IS_MAP(vm->apiStack[mapSlot]), "Slot must hold a map.");
+
+ ObjMap* map = AS_MAP(vm->apiStack[mapSlot]);
+ Value value = wrenMapGet(map, vm->apiStack[keySlot]);
+ if (IS_UNDEFINED(value)) {
+ value = NULL_VAL;
+ }
+
+ vm->apiStack[valueSlot] = value;
+}
+
+void wrenSetMapValue(WrenVM* vm, int mapSlot, int keySlot, int valueSlot)
+{
+ validateApiSlot(vm, mapSlot);
+ validateApiSlot(vm, keySlot);
+ validateApiSlot(vm, valueSlot);
+ ASSERT(IS_MAP(vm->apiStack[mapSlot]), "Must insert into a map.");
+
+ Value key = vm->apiStack[keySlot];
+ if (!validateKey(vm, key)) {
+ return;
+ }
+
+ Value value = vm->apiStack[valueSlot];
+ ObjMap* map = AS_MAP(vm->apiStack[mapSlot]);
+
+ wrenMapSet(vm, map, key, value);
+}
+
+void wrenRemoveMapValue(WrenVM* vm, int mapSlot, int keySlot,
+ int removedValueSlot)
+{
+ validateApiSlot(vm, mapSlot);
+ validateApiSlot(vm, keySlot);
+ ASSERT(IS_MAP(vm->apiStack[mapSlot]), "Slot must hold a map.");
+
+ Value key = vm->apiStack[keySlot];
+ if (!validateKey(vm, key)) {
+ return;
+ }
+
+ ObjMap* map = AS_MAP(vm->apiStack[mapSlot]);
+ Value removed = wrenMapRemoveKey(vm, map, key);
+ setSlot(vm, removedValueSlot, removed);
+}
+
void wrenGetVariable(WrenVM* vm, const char* module, const char* name,
int slot)
{
diff --git a/test/api/api_tests.c b/test/api/api_tests.c
index 82d4efea..cf2bd29c 100644
--- a/test/api/api_tests.c
+++ b/test/api/api_tests.c
@@ -40,6 +40,9 @@ WrenForeignMethodFn APITest_bindForeignMethod(
method = listsBindMethod(fullName);
if (method != NULL) return method;
+ method = mapsBindMethod(fullName);
+ if (method != NULL) return method;
+
method = newVMBindMethod(fullName);
if (method != NULL) return method;
diff --git a/test/api/api_tests.h b/test/api/api_tests.h
index b37f12a2..fd30dea8 100644
--- a/test/api/api_tests.h
+++ b/test/api/api_tests.h
@@ -16,6 +16,7 @@
#include "foreign_class.h"
#include "handle.h"
#include "lists.h"
+#include "maps.h"
#include "new_vm.h"
#include "reset_stack_after_call_abort.h"
#include "reset_stack_after_foreign_construct.h"
diff --git a/test/api/maps.c b/test/api/maps.c
new file mode 100644
index 00000000..7f497e2a
--- /dev/null
+++ b/test/api/maps.c
@@ -0,0 +1,130 @@
+#include
+
+#include "maps.h"
+
+static void newMap(WrenVM* vm)
+{
+ wrenSetSlotNewMap(vm, 0);
+}
+
+static void invalidInsert(WrenVM* vm)
+{
+ wrenSetSlotNewMap(vm, 0);
+
+ wrenEnsureSlots(vm, 3);
+ // Foreign Class is in slot 1
+ wrenSetSlotString(vm, 2, "England");
+ wrenSetMapValue(vm, 0, 1, 2); // expect this to cause errors
+}
+
+static void insert(WrenVM* vm)
+{
+ wrenSetSlotNewMap(vm, 0);
+
+ wrenEnsureSlots(vm, 3);
+
+ // Insert String
+ wrenSetSlotString(vm, 1, "England");
+ wrenSetSlotString(vm, 2, "London");
+ wrenSetMapValue(vm, 0, 1, 2);
+
+ // Insert Double
+ wrenSetSlotDouble(vm, 1, 1.0);
+ wrenSetSlotDouble(vm, 2, 42.0);
+ wrenSetMapValue(vm, 0, 1, 2);
+
+ // Insert Boolean
+ wrenSetSlotBool(vm, 1, false);
+ wrenSetSlotBool(vm, 2, true);
+ wrenSetMapValue(vm, 0, 1, 2);
+
+ // Insert Null
+ wrenSetSlotNull(vm, 1);
+ wrenSetSlotNull(vm, 2);
+ wrenSetMapValue(vm, 0, 1, 2);
+
+ // Insert List
+ wrenSetSlotString(vm, 1, "Empty");
+ wrenSetSlotNewList(vm, 2);
+ wrenSetMapValue(vm, 0, 1, 2);
+}
+
+static void remove(WrenVM* vm)
+{
+ wrenEnsureSlots(vm, 3);
+
+ wrenSetSlotString(vm, 2, "key");
+ wrenRemoveMapValue(vm, 1, 2, 0);
+}
+
+static void countWren(WrenVM* vm)
+{
+ int count = wrenGetMapCount(vm, 1);
+ wrenSetSlotDouble(vm, 0, count);
+}
+
+static void countAPI(WrenVM* vm)
+{
+ insert(vm);
+ int count = wrenGetMapCount(vm, 0);
+ wrenSetSlotDouble(vm, 0, count);
+}
+
+static void containsWren(WrenVM* vm)
+{
+ bool result = wrenGetMapContainsKey(vm, 1, 2);
+ wrenSetSlotBool(vm, 0, result);
+}
+
+
+static void containsAPI(WrenVM* vm)
+{
+ insert(vm);
+
+ wrenEnsureSlots(vm, 1);
+ wrenSetSlotString(vm, 1, "England");
+
+ bool result = wrenGetMapContainsKey(vm, 0, 1);
+ wrenSetSlotBool(vm, 0, result);
+}
+
+static void containsAPIFalse(WrenVM* vm)
+{
+ insert(vm);
+
+ wrenEnsureSlots(vm, 1);
+ wrenSetSlotString(vm, 1, "DefinitelyNotARealKey");
+
+ bool result = wrenGetMapContainsKey(vm, 0, 1);
+ wrenSetSlotBool(vm, 0, result);
+}
+
+
+WrenForeignMethodFn mapsBindMethod(const char* signature)
+{
+ if (strcmp(signature, "static Maps.newMap()") == 0) return newMap;
+ if (strcmp(signature, "static Maps.insert()") == 0) return insert;
+ if (strcmp(signature, "static Maps.remove(_)") == 0) return remove;
+ if (strcmp(signature, "static Maps.count(_)") == 0) return countWren;
+ if (strcmp(signature, "static Maps.count()") == 0) return countAPI;
+ if (strcmp(signature, "static Maps.contains()") == 0) return containsAPI;
+ if (strcmp(signature, "static Maps.containsFalse()") == 0) return containsAPIFalse;
+ if (strcmp(signature, "static Maps.contains(_,_)") == 0) return containsWren;
+ if (strcmp(signature, "static Maps.invalidInsert(_)") == 0) return invalidInsert;
+
+ return NULL;
+}
+
+void foreignAllocate(WrenVM* vm) {
+ wrenSetSlotNewForeign(vm, 0, 0, 0);
+}
+
+void mapBindClass(
+ const char* className, WrenForeignClassMethods* methods)
+{
+ if (strcmp(className, "ForeignClass") == 0)
+ {
+ methods->allocate = foreignAllocate;
+ return;
+ }
+}
diff --git a/test/api/maps.h b/test/api/maps.h
new file mode 100644
index 00000000..7bd3ae42
--- /dev/null
+++ b/test/api/maps.h
@@ -0,0 +1,5 @@
+#include "wren.h"
+
+WrenForeignMethodFn mapsBindMethod(const char* signature);
+void mapBindClass(
+ const char* className, WrenForeignClassMethods* methods);
diff --git a/test/api/maps.wren b/test/api/maps.wren
new file mode 100644
index 00000000..433a838b
--- /dev/null
+++ b/test/api/maps.wren
@@ -0,0 +1,68 @@
+class ForeignClass {
+ construct new() {}
+}
+
+class Maps {
+ foreign static newMap()
+ foreign static insert()
+ foreign static contains(map, key)
+ foreign static contains()
+ foreign static containsFalse()
+ foreign static count()
+ foreign static count(map)
+ foreign static remove(map)
+ foreign static invalidInsert(obj)
+}
+
+// map new + get/set API
+
+var map = Maps.newMap()
+System.print(map is Map) // expect: true
+System.print(map.count) // expect: 0
+
+var data = Maps.insert()
+System.print(data["England"]) // expect: London
+System.print(data["Empty"]) // expect: []
+System.print(data[1.0]) // expect: 42
+System.print(data[false]) // expect: true
+System.print(data[null]) // expect: null
+
+// remove API
+
+var removed = Maps.remove({ "key":"value", "other":"data" })
+System.print(removed) // expect: value
+
+var removedNone = Maps.remove({})
+System.print(removedNone) // expect: null
+
+// count API
+
+var countMap = { "key":"value", "other":"data", 4:"number key" }
+System.print(Maps.count(countMap)) // expect: 3
+Maps.remove(countMap) //remove using API
+System.print(Maps.count(countMap)) // expect: 2
+countMap.remove("other") //remove wren side
+System.print(Maps.count(countMap)) // expect: 1
+
+var countAPI = Maps.count()
+System.print(countAPI) // expect: 5
+
+//contains key API
+
+var containsMap = { "key":"value", "other":"data", 4:"number key" }
+System.print(Maps.contains(containsMap, "key")) // expect: true
+System.print(Maps.contains(containsMap, "fake")) // expect: false
+System.print(Maps.contains(containsMap, "other")) // expect: true
+
+Maps.remove(containsMap) //remove using API
+System.print(Maps.contains(containsMap, "key")) // expect: false
+
+containsMap.remove("other") //remove wren side
+System.print(Maps.contains(containsMap, "other")) // expect: false
+
+System.print(Maps.contains()) // expect: true
+System.print(Maps.containsFalse()) // expect: false
+
+//
+
+Maps.invalidInsert(ForeignClass.new()) // expect runtime error: Key must be a value type.
diff --git a/test/api/slots.c b/test/api/slots.c
index 64c625e2..b897070a 100644
--- a/test/api/slots.c
+++ b/test/api/slots.c
@@ -82,10 +82,11 @@ static void slotTypes(WrenVM* vm)
wrenGetSlotType(vm, 1) == WREN_TYPE_BOOL &&
wrenGetSlotType(vm, 2) == WREN_TYPE_FOREIGN &&
wrenGetSlotType(vm, 3) == WREN_TYPE_LIST &&
- wrenGetSlotType(vm, 4) == WREN_TYPE_NULL &&
- wrenGetSlotType(vm, 5) == WREN_TYPE_NUM &&
- wrenGetSlotType(vm, 6) == WREN_TYPE_STRING &&
- wrenGetSlotType(vm, 7) == WREN_TYPE_UNKNOWN;
+ wrenGetSlotType(vm, 4) == WREN_TYPE_MAP &&
+ wrenGetSlotType(vm, 5) == WREN_TYPE_NULL &&
+ wrenGetSlotType(vm, 6) == WREN_TYPE_NUM &&
+ wrenGetSlotType(vm, 7) == WREN_TYPE_STRING &&
+ wrenGetSlotType(vm, 8) == WREN_TYPE_UNKNOWN;
wrenSetSlotBool(vm, 0, result);
}
@@ -166,16 +167,22 @@ static void getListElement(WrenVM* vm)
wrenGetListElement(vm, 1, index, 0);
}
+static void getMapValue(WrenVM* vm)
+{
+ wrenGetMapValue(vm, 1, 2, 0);
+}
+
WrenForeignMethodFn slotsBindMethod(const char* signature)
{
if (strcmp(signature, "static Slots.noSet") == 0) return noSet;
if (strcmp(signature, "static Slots.getSlots(_,_,_,_,_)") == 0) return getSlots;
if (strcmp(signature, "static Slots.setSlots(_,_,_,_,_)") == 0) return setSlots;
- if (strcmp(signature, "static Slots.slotTypes(_,_,_,_,_,_,_)") == 0) return slotTypes;
+ if (strcmp(signature, "static Slots.slotTypes(_,_,_,_,_,_,_,_)") == 0) return slotTypes;
if (strcmp(signature, "static Slots.ensure()") == 0) return ensure;
if (strcmp(signature, "static Slots.ensureOutsideForeign()") == 0) return ensureOutsideForeign;
if (strcmp(signature, "static Slots.getListCount(_)") == 0) return getListCount;
if (strcmp(signature, "static Slots.getListElement(_,_)") == 0) return getListElement;
+ if (strcmp(signature, "static Slots.getMapValue(_,_)") == 0) return getMapValue;
return NULL;
}
diff --git a/test/api/slots.wren b/test/api/slots.wren
index 24767898..045cced8 100644
--- a/test/api/slots.wren
+++ b/test/api/slots.wren
@@ -2,11 +2,12 @@ class Slots {
foreign static noSet
foreign static getSlots(bool, num, string, bytes, value)
foreign static setSlots(a, b, c, d, e)
- foreign static slotTypes(bool, foreignObj, list, nullObj, num, string, unknown)
+ foreign static slotTypes(bool, foreignObj, list, map, nullObj, num, string, unknown)
foreign static ensure()
foreign static ensureOutsideForeign()
foreign static getListCount(list)
foreign static getListElement(list, index)
+ foreign static getMapValue(map, key)
}
foreign class ForeignType {
@@ -24,7 +25,7 @@ System.print(Slots.getSlots(true, "by\0te", 1.5, "str", value) == value)
System.print(Slots.setSlots(value, 0, 0, 0, 0) == value)
// expect: true
-System.print(Slots.slotTypes(false, ForeignType.new(), [], null, 1.2, "str", 1..2))
+System.print(Slots.slotTypes(false, ForeignType.new(), [], {}, null, 1.2, "str", 1..2))
// expect: true
System.print(Slots.ensure())
@@ -37,3 +38,14 @@ var ducks = ["Huey", "Dewey", "Louie"]
System.print(Slots.getListCount(ducks)) // expect: 3
System.print(Slots.getListElement(ducks, 0)) // expect: Huey
System.print(Slots.getListElement(ducks, 1)) // expect: Dewey
+
+var capitals = {
+ "England": "London",
+ "Scotland": "Edinburgh",
+ "Wales": "Cardiff",
+ "N. Ireland": "Belfast"
+}
+
+System.print(Slots.getMapValue(capitals, "England")) // expect: London
+System.print(Slots.getMapValue(capitals, "Wales")) // expect: Cardiff
+System.print(Slots.getMapValue(capitals, "S. Ireland")) // expect: null