Support only some types as JsAssoc types (#8627)

This commit is contained in:
Alexander Ivanov
2018-11-23 16:05:15 +02:00
committed by Andreas Rumpf
parent 562d185cb7
commit 88d707cb88
2 changed files with 46 additions and 45 deletions

View File

@@ -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
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 `[]`*[V](obj: JsAssoc[string, V], field: cstring): 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

View File

@@ -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()