summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorZahary Karadjov <zahary@gmail.com>2017-04-09 23:04:55 +0300
committerZahary Karadjov <zahary@gmail.com>2017-04-09 23:04:55 +0300
commit48a1a54d1ca0f17676ddddbb10a48a5406319fe5 (patch)
treefa7beb8787f6fa0bb5e5fadd5e7cf10b93d9c0a9
parent987b522071fcf35b59d633bafd7d312cdb385c1f (diff)
downloadNim-48a1a54d1ca0f17676ddddbb10a48a5406319fe5.tar.gz
improve the usability of the jsffi module
* All JavaScript operators are usable with JsObject
* The dot operators will use native JavaScript strings
* Results returned from dot calls are consired discardable
-rw-r--r--lib/js/jsffi.nim62
-rw-r--r--tests/js/tjsffi.nim52
2 files changed, 104 insertions, 10 deletions
diff --git a/lib/js/jsffi.nim b/lib/js/jsffi.nim
index 646b79fe5..7aa80b39b 100644
--- a/lib/js/jsffi.nim
+++ b/lib/js/jsffi.nim
@@ -79,6 +79,9 @@ type
   NotString = concept c
     c isnot string
 
+var jsarguments* {.importc: "arguments", nodecl}: JsObject
+  ## JavaScript's arguments pseudo-variable
+
 # New
 proc newJsObject*: JsObject {. importcpp: "{@}" .}
   ## Creates a new empty JsObject
@@ -93,18 +96,62 @@ proc hasOwnProperty*(x: JsObject, prop: cstring): bool
 proc jsTypeOf*(x: JsObject): cstring {. importcpp: "typeof(#)" .}
   ## Returns the name of the JsObject's JavaScript type as a cstring.
 
+proc jsnew*(x: auto): JsObject {.importcpp: "(new #)".}
+  ## Turns a regular function call into an invocation of the
+  ## JavaScript's `new` operator
+
+proc jsdelete*(x: auto): JsObject {.importcpp: "(delete #)".}
+  ## JavaScript's `delete` operator
+
 # Conversion to and from JsObject
 proc to*(x: JsObject, T: typedesc): T {. importcpp: "(#)" .}
   ## Converts a JsObject `x` to type `T`.
+
 proc toJs*[T](val: T): JsObject {. importcpp: "(#)" .}
   ## Converts a value of any type to type JsObject
 
+template toJs*(s: string): JsObject = cstring(s).toJs
+
+proc `&`*(a, b: cstring): cstring {.importcpp: "(# + #)".}
+  ## Concatenation operator for JavaScript strings
+
+proc `+`  *(x, y: JsObject): JsObject {. importcpp: "(# + #)" .}
+proc `-`  *(x, y: JsObject): JsObject {. importcpp: "(# - #)" .}
+proc `*`  *(x, y: JsObject): JsObject {. importcpp: "(# * #)" .}
+proc `/`  *(x, y: JsObject): JsObject {. importcpp: "(# / #)" .}
+proc `%`  *(x, y: JsObject): JsObject {. importcpp: "(# % #)" .}
+proc `+=` *(x, y: JsObject): JsObject {. importcpp: "(# += #)", discardable .}
+proc `-=` *(x, y: JsObject): JsObject {. importcpp: "(# -= #)", discardable .}
+proc `*=` *(x, y: JsObject): JsObject {. importcpp: "(# *= #)", discardable .}
+proc `/=` *(x, y: JsObject): JsObject {. importcpp: "(# /= #)", discardable .}
+proc `%=` *(x, y: JsObject): JsObject {. importcpp: "(# %= #)", discardable .}
+proc `++` *(x: JsObject): JsObject    {. importcpp: "(++#)" .}
+proc `--` *(x: JsObject): JsObject    {. importcpp: "(--#)" .}
+# proc `==` *(x, y: JsObject): JsObject {. importcpp: "(# == #)" .}
+# proc `===`*(x, y: JsObject): JsObject {. importcpp: "(# === #)" .}
+# proc `!=` *(x, y: JsObject): JsObject {. importcpp: "(# != #)" .}
+# proc `!==`*(x, y: JsObject): JsObject {. importcpp: "(# !== #)" .}
+proc `>`  *(x, y: JsObject): JsObject {. importcpp: "(# > #)" .}
+proc `<`  *(x, y: JsObject): JsObject {. importcpp: "(# < #)" .}
+proc `>=` *(x, y: JsObject): JsObject {. importcpp: "(# >= #)" .}
+proc `<=` *(x, y: JsObject): JsObject {. importcpp: "(# <= #)" .}
+proc `and`*(x, y: JsObject): JsObject {. importcpp: "(# && #)" .}
+proc `or` *(x, y: JsObject): JsObject {. importcpp: "(# || #)" .}
+proc `not`*(x: JsObject): JsObject    {. importcpp: "(!#)" .}
+proc `in` *(x, y: JsObject): JsObject {. importcpp: "(# in #)" .}
+
 proc `[]`*(obj: JsObject, field: cstring): JsObject {. importcpp: getImpl .}
   ## Return the value of a property of name `field` from a JsObject `obj`.
 
+proc `[]`*(obj: JsObject, field: int): JsObject {. importcpp: getImpl .}
+  ## Return the value of a property of name `field` from a JsObject `obj`.
+
 proc `[]=`*[T](obj: JsObject, field: cstring, val: T) {. importcpp: setImpl .}
   ## Set the value of a property of name `field` in a JsObject `obj` to `v`.
 
+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: NotString, V](obj: JsAssoc[K, V], field: K): V
   {. importcpp: getImpl .}
   ## Return the value of a property of name `field` from a JsAssoc `obj`.
@@ -171,8 +218,9 @@ macro `.=`*(obj: JsObject, field: static[cstring], value: untyped): untyped =
         {. importcpp: `importString`, gensym .}
       helper(`obj`, `value`)
 
-macro `.()`*(obj: JsObject, field: static[cstring],
-    args: varargs[JsObject, toJs]): JsObject =
+macro `.()`*(obj: JsObject,
+             field: static[cstring],
+             args: varargs[JsObject, toJs]): JsObject =
   ## Experimental "method call" operator for type JsObject.
   ## Takes the name of a method of the JavaScript object (`field`) and calls
   ## it with `args` as arguments, returning a JsObject (which may be discarded,
@@ -198,7 +246,7 @@ macro `.()`*(obj: JsObject, field: static[cstring],
     importString = "#." & mangledNames[$field] & "(@)"
   result = quote do:
     proc helper(o: JsObject): JsObject
-      {. importcpp: `importString`, gensym .}
+      {. importcpp: `importString`, gensym, discardable .}
     helper(`obj`)
   for idx in 0 ..< args.len:
     let paramName = newIdentNode(!("param" & $idx))
@@ -206,7 +254,7 @@ macro `.()`*(obj: JsObject, field: static[cstring],
     result[1].add args[idx].copyNimTree
 
 macro `.`*[K: string | cstring, V](obj: JsAssoc[K, V],
-  field: static[cstring]): V =
+                                   field: static[cstring]): V =
   ## Experimental dot accessor (get) for type JsAssoc.
   ## Returns the value of a property of name `field` from a JsObject `x`.
   var importString: string
@@ -222,7 +270,8 @@ macro `.`*[K: string | cstring, V](obj: JsAssoc[K, V],
     helper(`obj`)
 
 macro `.=`*[K: string | cstring, V](obj: JsAssoc[K, V],
-  field: static[cstring], value: V): untyped =
+                                    field: static[cstring],
+                                    value: V): untyped =
   ## Experimental dot accessor (set) for type JsAssoc.
   ## Sets the value of a property of name `field` in a JsObject `x` to `value`.
   var importString: string
@@ -238,7 +287,8 @@ macro `.=`*[K: string | cstring, V](obj: JsAssoc[K, V],
     helper(`obj`, `value`)
 
 macro `.()`*[K: string | cstring, V: proc](obj: JsAssoc[K, V],
-  field: static[cstring], args: varargs[untyped]): auto =
+                                           field: static[cstring],
+                                           args: varargs[untyped]): auto =
   ## Experimental "method call" operator for type JsAssoc.
   ## Takes the name of a method of the JavaScript object (`field`) and calls
   ## it with `args` as arguments. Here, everything is typechecked, so you do not
diff --git a/tests/js/tjsffi.nim b/tests/js/tjsffi.nim
index 71eb211e3..be8e2efb7 100644
--- a/tests/js/tjsffi.nim
+++ b/tests/js/tjsffi.nim
@@ -14,10 +14,15 @@ true
 true
 true
 true
-true'''
+true
+3
+2
+12
+Event { name: 'click: test' }
+Event { name: 'reloaded: test' }'''
 """
 
-import macros, jsffi
+import macros, jsffi, jsconsole
 
 # Tests for JsObject
 # Test JsObject []= and []
@@ -55,8 +60,8 @@ block:
 block:
   proc test(): bool =
     let obj = newJsObject()
-    obj.`?!$` = proc(x, y, z: int, t: string): string = t & $(x + y + z)
-    obj.`?!$`(1, 2, 3, "Result is: ").to(string) == "Result is: 6"
+    obj.`?!$` = proc(x, y, z: int, t: cstring): cstring = t & $(x + y + z)
+    obj.`?!$`(1, 2, 3, "Result is: ").to(cstring) == "Result is: 6"
   echo test()
 
 # Test JsObject []()
@@ -265,3 +270,42 @@ block:
     let obj = TestObject(a: 9, onWhatever: bindMethod(handleWhatever))
     obj.onWhatever(1) == 10
   echo test()
+
+block:
+  {.emit: "function jsProc(n) { return n; }" .}
+  proc jsProc(x: int32): JsObject {.importc: "jsProc".}
+
+  proc test() =
+    var x = jsProc(1)
+    var y = jsProc(2)
+    console.log x + y
+    console.log ++x
+
+    x += jsProc(10)
+    console.log x
+
+  test()
+
+import macros
+
+block:
+  {.emit:
+  """
+  function Event(name) { this.name = name; }
+  function on(eventName, eventHandler) { eventHandler(new Event(eventName + ": test")); }
+  var jslib = { "on": on };
+  """
+  .}
+
+  type Event = object
+    name: cstring
+
+  proc on(event: cstring, handler: proc) {.importc: "on".}
+  var jslib {.importc: "jslib", nodecl.}: JsObject
+
+  on("click") do (e: Event):
+    console.log e
+
+  jslib.on("reloaded") do:
+    console.log jsarguments[0]
+