diff options
-rw-r--r-- | lib/js/jsffi.nim | 67 | ||||
-rw-r--r-- | tests/js/tjsffi.nim | 24 |
2 files changed, 46 insertions, 45 deletions
diff --git a/lib/js/jsffi.nim b/lib/js/jsffi.nim index 307fe2382..e1c59803d 100644 --- a/lib/js/jsffi.nim +++ b/lib/js/jsffi.nim @@ -69,10 +69,25 @@ template mangleJsName(name: cstring): cstring = inc nameCounter "mangledName" & $nameCounter +# only values that can be mapped 1 to 1 with cstring should be keys: they have an injective function with cstring + +proc toJsKey*[T: SomeInteger](text: cstring, t: type T): T {.importcpp: "parseInt(#)".} + +proc toJsKey*[T: enum](text: cstring, t: type T): T = + T(text.toJsKey(int)) + +proc toJsKey*(text: cstring, t: type cstring): cstring = + text + +proc toJsKey*[T: SomeFloat](text: cstring, t: type T): T {.importcpp: "parseFloat(#)".} + type + JsKey* = concept a, type T + cstring.toJsKey(T) is type(a) + JsObject* = ref object of JsRoot ## Dynamically typed wrapper around a JavaScript object. - JsAssoc*[K, V] = ref object of JsRoot + JsAssoc*[K: JsKey, V] = ref object of JsRoot ## Statically typed wrapper around a JavaScript object. js* = JsObject @@ -104,7 +119,7 @@ type proc newJsObject*: JsObject {. importcpp: "{@}" .} ## Creates a new empty JsObject -proc newJsAssoc*[K, V]: JsAssoc[K, V] {. importcpp: "{@}" .} +proc newJsAssoc*[K: JsKey, V]: JsAssoc[K, V] {. importcpp: "{@}" .} ## Creates a new empty JsAssoc with key type `K` and value type `V`. # Checks @@ -176,21 +191,19 @@ proc `[]=`*[T](obj: JsObject, field: cstring, val: T) {. importcpp: setImpl .} proc `[]=`*[T](obj: JsObject, field: int, val: T) {. importcpp: setImpl .} ## Set the value of a property of name `field` in a JsObject `obj` to `v`. -proc `[]`*[K: not string, V](obj: JsAssoc[K, V], field: K): V - {. importcpp: getImpl .} - ## Return the value of a property of name `field` from a JsAssoc `obj`. - -proc `[]`*[V](obj: JsAssoc[string, V], field: cstring): V +proc `[]`*[K: JsKey, V](obj: JsAssoc[K, V], field: K): V {. importcpp: getImpl .} ## Return the value of a property of name `field` from a JsAssoc `obj`. -proc `[]=`*[K: not string, V](obj: JsAssoc[K, V], field: K, val: V) +proc `[]=`*[K: JsKey, V](obj: JsAssoc[K, V], field: K, val: V) {. importcpp: setImpl .} ## Set the value of a property of name `field` in a JsAssoc `obj` to `v`. -proc `[]=`*[V](obj: JsAssoc[string, V], field: cstring, val: V) - {. importcpp: setImpl .} - ## Set the value of a property of name `field` in a JsAssoc `obj` to `v`. +proc `[]`*[V](obj: JsAssoc[cstring, V], field: string): V = + obj[cstring(field)] + +proc `[]=`*[V](obj: JsAssoc[cstring, V], field: string, val: V) = + obj[cstring(field)] = val proc `==`*(x, y: JsRoot): bool {. importcpp: "(# === #)" .} ## Compare two JsObjects or JsAssocs. Be careful though, as this is comparison @@ -277,7 +290,7 @@ macro `.()`*(obj: JsObject, result[0][3].add newIdentDefs(paramName, newIdentNode(!"JsObject")) result[1].add args[idx].copyNimTree -macro `.`*[K: string | cstring, V](obj: JsAssoc[K, V], +macro `.`*[K: cstring, V](obj: JsAssoc[K, V], field: untyped): V = ## Experimental dot accessor (get) for type JsAssoc. ## Returns the value of a property of name `field` from a JsObject `x`. @@ -293,7 +306,7 @@ macro `.`*[K: string | cstring, V](obj: JsAssoc[K, V], {. importcpp: `importString`, gensym .} helper(`obj`) -macro `.=`*[K: string | cstring, V](obj: JsAssoc[K, V], +macro `.=`*[K: cstring, V](obj: JsAssoc[K, V], field: untyped, value: V): untyped = ## Experimental dot accessor (set) for type JsAssoc. @@ -310,7 +323,7 @@ macro `.=`*[K: string | cstring, V](obj: JsAssoc[K, V], {. importcpp: `importString`, gensym .} helper(`obj`, `value`) -macro `.()`*[K: string | cstring, V: proc](obj: JsAssoc[K, V], +macro `.()`*[K: cstring, V: proc](obj: JsAssoc[K, V], field: untyped, args: varargs[untyped]): auto = ## Experimental "method call" operator for type JsAssoc. @@ -354,24 +367,18 @@ iterator keys*(obj: JsObject): cstring = yield k {.emit: "}".} -iterator pairs*[K, V](assoc: JsAssoc[K, V]): (K,V) = +iterator pairs*[K: JsKey, V](assoc: JsAssoc[K, V]): (K,V) = ## Yields tuples of type ``(K, V)``, with the first entry ## being a `key` in the JsAssoc and the second being its corresponding value. - when K is string: - var k: cstring - else: - var k: K + var k: cstring var v: V {.emit: "for (var `k` in `assoc`) {".} {.emit: " if (!`assoc`.hasOwnProperty(`k`)) continue;".} {.emit: " `v`=`assoc`[`k`];".} - when K is string: - yield ($k, v) - else: - yield (k, v) + yield (k.toJsKey(K), v) {.emit: "}".} -iterator items*[K,V](assoc: JSAssoc[K,V]): V = +iterator items*[K, V](assoc: JSAssoc[K, V]): V = ## Yields the `values` in a JsAssoc. var v: V {.emit: "for (var k in `assoc`) {".} @@ -380,18 +387,12 @@ iterator items*[K,V](assoc: JSAssoc[K,V]): V = yield v {.emit: "}".} -iterator keys*[K,V](assoc: JSAssoc[K,V]): K = +iterator keys*[K: JsKey, V](assoc: JSAssoc[K, V]): K = ## Yields the `keys` in a JsAssoc. - when K is string: - var k: cstring - else: - var k: K + var k: cstring {.emit: "for (var `k` in `assoc`) {".} {.emit: " if (!`assoc`.hasOwnProperty(`k`)) continue;".} - when K is string: - yield $k - else: - yield k + yield k.toJsKey(K) {.emit: "}".} # Literal generation diff --git a/tests/js/tjsffi.nim b/tests/js/tjsffi.nim index 213d05964..2420c60f6 100644 --- a/tests/js/tjsffi.nim +++ b/tests/js/tjsffi.nim @@ -154,7 +154,7 @@ block: # Test JsAssoc .= and . block: proc test(): bool = - let obj = newJsAssoc[string, int]() + let obj = newJsAssoc[cstring, int]() var working = true obj.a = 11 obj.`$!&` = 42 @@ -168,7 +168,7 @@ block: # Test JsAssoc .() block: proc test(): bool = - let obj = newJsAssoc[string, proc(e: int): int]() + let obj = newJsAssoc[cstring, proc(e: int): int]() obj.a = proc(e: int): int = e * e obj.a(10) == 100 echo test() @@ -176,7 +176,7 @@ block: # Test JsAssoc []() block: proc test(): bool = - let obj = newJsAssoc[string, proc(e: int): int]() + let obj = newJsAssoc[cstring, proc(e: int): int]() obj.a = proc(e: int): int = e * e let call = obj["a"] call(10) == 100 @@ -185,7 +185,7 @@ block: # Test JsAssoc Iterators block: proc testPairs(): bool = - let obj = newJsAssoc[string, int]() + let obj = newJsAssoc[cstring, int]() var working = true obj.a = 10 obj.b = 20 @@ -202,7 +202,7 @@ block: return false working proc testItems(): bool = - let obj = newJsAssoc[string, int]() + let obj = newJsAssoc[cstring, int]() var working = true obj.a = 10 obj.b = 20 @@ -211,13 +211,13 @@ block: working = working and v in [10, 20, 30] working proc testKeys(): bool = - let obj = newJsAssoc[string, int]() + let obj = newJsAssoc[cstring, int]() var working = true obj.a = 10 obj.b = 20 obj.c = 30 for v in obj.keys: - working = working and v in ["a", "b", "c"] + working = working and v in [cstring"a", cstring"b", cstring"c"] working proc test(): bool = testPairs() and testItems() and testKeys() echo test() @@ -226,8 +226,8 @@ block: block: proc test(): bool = {. emit: "var comparison = {a: 22, b: 55};" .} - var comparison {. importcpp, nodecl .}: JsAssoc[string, int] - let obj = newJsAssoc[string, int]() + var comparison {. importcpp, nodecl .}: JsAssoc[cstring, int] + let obj = newJsAssoc[cstring, int]() obj.a = 22 obj.b = 55 obj.a == comparison.a and obj.b == comparison.b @@ -237,15 +237,15 @@ block: block: proc test(): bool = {. emit: "var comparison = {a: 22, b: 55};" .} - var comparison {. importcpp, nodecl .}: JsAssoc[string, int] - let obj = JsAssoc[string, int]{ a: 22, b: 55 } + var comparison {. importcpp, nodecl .}: JsAssoc[cstring, int] + let obj = JsAssoc[cstring, int]{ a: 22, b: 55 } var working = true working = working and compiles(JsAssoc[int, int]{ 1: 22, 2: 55 }) working = working and comparison.a == obj.a and comparison.b == obj.b working = working and - not compiles(JsAssoc[string, int]{ a: "test" }) + not compiles(JsAssoc[cstring, int]{ a: "test" }) working echo test() |