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