From 8353625a7f5b57128a2642b18e552fb1a9dc191c Mon Sep 17 00:00:00 2001 From: hlaaftana Date: Tue, 9 Feb 2021 10:59:13 +0300 Subject: [PATCH 1/3] tests and docs for call operator --- doc/manual_experimental.rst | 35 +++++++++++++++++++++- tests/specialops/tcallops.nim | 39 ++++++++++++++++++++++++ tests/specialops/tcallprecedence.nim | 44 ++++++++++++++++++++++++++++ 3 files changed, 117 insertions(+), 1 deletion(-) create mode 100644 tests/specialops/tcallops.nim create mode 100644 tests/specialops/tcallprecedence.nim diff --git a/doc/manual_experimental.rst b/doc/manual_experimental.rst index 63108c8964da6..03f3db949ef5e 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,39 @@ 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) + doAssert not compiles(a.b) + + 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 0000000000000..0508a37a1594f --- /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 0000000000000..6116f83d53b9e --- /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) From 60c69362b650d0530f831aeb4be06d9665865c63 Mon Sep 17 00:00:00 2001 From: hlaaftana Date: Tue, 9 Feb 2021 11:02:35 +0300 Subject: [PATCH 2/3] fix leftover --- doc/manual_experimental.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/doc/manual_experimental.rst b/doc/manual_experimental.rst index 03f3db949ef5e..c3d221a1b0347 100644 --- a/doc/manual_experimental.rst +++ b/doc/manual_experimental.rst @@ -451,7 +451,6 @@ to use this operator. let b = 2 doAssert b(a) == `()`(b, a) doAssert a.b == `()`(b, a) - doAssert not compiles(a.b) block: let a = 1.0 From 006ffb67efa84b95f8e0d02689f760cdce7040fa Mon Sep 17 00:00:00 2001 From: hlaaftana Date: Tue, 9 Feb 2021 11:10:29 +0300 Subject: [PATCH 3/3] add extra dot test --- tests/specialops/tdotops.nim | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/specialops/tdotops.nim b/tests/specialops/tdotops.nim index b1c75ab33aff5..ca5eee6657d95 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