summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--doc/manual_experimental.rst34
-rw-r--r--tests/specialops/tcallops.nim39
-rw-r--r--tests/specialops/tcallprecedence.nim44
-rw-r--r--tests/specialops/tdotops.nim8
4 files changed, 124 insertions, 1 deletions
diff --git a/doc/manual_experimental.rst b/doc/manual_experimental.rst
index 63108c896..c3d221a1b 100644
--- a/doc/manual_experimental.rst
+++ b/doc/manual_experimental.rst
@@ -408,7 +408,7 @@ The matched dot operators can be symbols of any callable kind (procs,
 templates and macros), depending on the desired effect:
 
 .. code-block:: nim
-  template `.` (js: PJsonNode, field: untyped): JSON = js[astToStr(field)]
+  template `.`(js: PJsonNode, field: untyped): JSON = js[astToStr(field)]
 
   var js = parseJson("{ x: 1, y: 2}")
   echo js.x # outputs 1
@@ -434,6 +434,38 @@ This operator will be matched against assignments to missing fields.
 .. code-block:: nim
   a.b = c # becomes `.=`(a, b, c)
 
+Call operator
+-------------
+The call operator, `()`, matches all kinds of unresolved calls and takes
+precedence over dot operators, however it does not match missing overloads
+for existing routines. The experimental `callOperator` switch must be enabled
+to use this operator.
+
+.. code-block:: nim
+  {.experimental: "callOperator".}
+
+  template `()`(a: int, b: float): untyped = $(a, b)
+
+  block:
+    let a = 1.0
+    let b = 2
+    doAssert b(a) == `()`(b, a)
+    doAssert a.b == `()`(b, a)
+
+  block:
+    let a = 1.0
+    proc b(): int = 2
+    doAssert not compiles(b(a))
+    doAssert not compiles(a.b) # `()` not called
+
+  block:
+    let a = 1.0
+    proc b(x: float): int = int(x + 1)
+    let c = 3.0
+
+    doAssert not compiles(a.b(c)) # gives a type mismatch error same as b(a, c)
+    doAssert (a.b)(c) == `()`(a.b, c)
+
 
 Not nil annotation
 ==================
diff --git a/tests/specialops/tcallops.nim b/tests/specialops/tcallops.nim
new file mode 100644
index 000000000..0508a37a1
--- /dev/null
+++ b/tests/specialops/tcallops.nim
@@ -0,0 +1,39 @@
+import macros
+
+{.experimental: "callOperator".}
+
+type Foo[T: proc] = object
+  callback: T
+
+macro `()`(foo: Foo, args: varargs[untyped]): untyped =
+  result = newCall(newDotExpr(foo, ident"callback"))
+  for a in args:
+    result.add(a)
+
+var f1Calls = 0
+var f = Foo[proc()](callback: proc() = inc f1Calls)
+doAssert f1Calls == 0
+f()
+doAssert f1Calls == 1
+var f2Calls = 0
+f.callback = proc() = inc f2Calls
+doAssert f2Calls == 0
+f()
+doAssert f2Calls == 1
+
+let g = Foo[proc (x: int): int](callback: proc (x: int): int = x * 2 + 1)
+doAssert g(15) == 31
+
+proc `()`(args: varargs[string]): string =
+  result = "("
+  for a in args: result.add(a)
+  result.add(')')
+
+let a = "1"
+let b = "2"
+let c = "3"
+
+doAssert a(b) == "(12)"
+doAssert a.b(c) == `()`(b, a, c)
+doAssert (a.b)(c) == `()`(a.b, c)
+doAssert `()`(a.b, c) == `()`(`()`(b, a), c)
diff --git a/tests/specialops/tcallprecedence.nim b/tests/specialops/tcallprecedence.nim
new file mode 100644
index 000000000..6116f83d5
--- /dev/null
+++ b/tests/specialops/tcallprecedence.nim
@@ -0,0 +1,44 @@
+import macros
+
+{.experimental: "dotOperators".}
+{.experimental: "callOperator".}
+
+block:
+  template `.()`(foo: int, args: varargs[untyped]): untyped {.used.} =
+    ".()"
+
+  template `()`(foo: int, args: varargs[untyped]): untyped =
+    "()"
+
+  let a = (b: 1)
+  let c = 3
+
+  doAssert a.b(c) == "()"
+  doAssert not compiles(a(c))
+  doAssert (a.b)(c) == "()"
+
+macro `()`(args: varargs[typed]): untyped =
+  result = newLit("() " & args.treeRepr)
+
+macro `.`(args: varargs[typed]): untyped =
+  result = newLit(". " & args.treeRepr)
+
+block:
+  let a = 1
+  let b = 2
+  doAssert a.b == `()`(b, a)
+
+block:
+  let a = 1
+  proc b(): int {.used.} = 2
+  doAssert a.b == `.`(a, b)
+
+block:
+  let a = 1
+  proc b(x: int): int = x + 1
+  let c = 3
+
+  doAssert a.b(c) == `.`(a, b, c)
+  doAssert a(b) == `()`(a, b)
+  doAssert (a.b)(c) == `()`(a.b, c)
+  doAssert a.b == b(a)
diff --git a/tests/specialops/tdotops.nim b/tests/specialops/tdotops.nim
index b1c75ab33..ca5eee665 100644
--- a/tests/specialops/tdotops.nim
+++ b/tests/specialops/tdotops.nim
@@ -17,6 +17,14 @@ one param call to c with 10
 '''
 """
 
+block:
+  type Foo = object
+  var a: Foo
+  template `.`(a: Foo, b: untyped): untyped = astToStr(b)
+  template callme(a, f): untyped = a.f
+  doAssert callme(a, f2) == "f2" # not `f`
+  doAssert a.callme(f3) == "f3"
+
 type
   T1 = object
     x*: int