tests and docs for call operator (#16980)

* tests and docs for call operator

* fix leftover

* add extra dot test
This commit is contained in:
hlaaftana 2021-02-09 14:18:16 +03:00 • committed by GitHub
commit aac8f67573
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 124 additions and 1 deletions

View file

@ -408,7 +408,7 @@ The matched dot operators can be symbols of any callable kind (procs,
templates and macros), depending on the desired effect: templates and macros), depending on the desired effect:
.. code-block:: nim .. 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}") var js = parseJson("{ x: 1, y: 2}")
echo js.x # outputs 1 echo js.x # outputs 1
@ -434,6 +434,38 @@ This operator will be matched against assignments to missing fields.
.. code-block:: nim .. code-block:: nim
a.b = c # becomes `.=`(a, b, c) 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 Not nil annotation
================== ==================

View file

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

View file

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

View file

@ -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 type
T1 = object T1 = object
x*: int x*: int