From 1c669ab0e1c016268562cc2539cf4f48acb43544 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Fri, 3 Aug 2018 19:08:44 -0700 Subject: [PATCH] add extractGeneric: extractGeneric(Foo2[float, string], 0) is float --- doc/manual.rst | 3 ++- lib/pure/sugar.nim | 31 +++++++++++++++++++++++++++++++ tests/stdlib/tsugar.nim | 21 +++++++++++++++++++++ 3 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 tests/stdlib/tsugar.nim diff --git a/doc/manual.rst b/doc/manual.rst index abdc4ce695fdb..059afc90b19ad 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -4351,7 +4351,7 @@ type classes are called `bind many`:idx: types. Procs written with the implicitly generic style will often need to refer to the type parameters of the matched generic type. They can be easily accessed using -the dot syntax: +``sugar.extractGeneric`` or the dot syntax: .. code-block:: nim type Matrix[T, Rows, Columns] = object @@ -4359,6 +4359,7 @@ the dot syntax: proc `[]`(m: Matrix, row, col: int): Matrix.T = m.data[col * high(Matrix.Columns) + row] + # we could've also used ``extractGeneric(Matrix, 0)`` Alternatively, the `type` operator can be used over the proc params for similar effect when anonymous or distinct type classes are used. diff --git a/lib/pure/sugar.nim b/lib/pure/sugar.nim index 258b40191ab9f..fc19b4aa58d7f 100644 --- a/lib/pure/sugar.nim +++ b/lib/pure/sugar.nim @@ -198,3 +198,34 @@ macro dump*(x: typed): untyped = let r = quote do: debugEcho `s`, " = ", `x` return r + +macro extractGeneric*(T: typedesc, index:static[int]): untyped = + ## extract generic type numbered ``index`` used to construct ``T``. Note: + ## ``-1`` returns ``Foo`` in ``Foo[T]`` + runnableExamples: + type Foo[T1, T2]=object + doAssert extractGeneric(Foo[float, string], 0) is float + doAssert extractGeneric(Foo[float, string], 1) is string + doAssert extractGeneric(Foo[float, string], -1) is Foo + + var impl = getTypeImpl(T) + expectKind(impl, nnkBracketExpr) + impl = impl[1] + while true: + case impl.kind + of nnkSym: + impl = impl.getImpl + continue + of nnkTypeDef: + impl = impl[2] + continue + of nnkBracketExpr: + if index == -1: + impl=impl[0] #return `Foo` in `Foo[T]` + else: + impl=impl[1+index] #return `T` in `Foo[T]` (when index = 0) + break + else: + error "internal error: impl.kind: " & $impl.kind + impl + diff --git a/tests/stdlib/tsugar.nim b/tests/stdlib/tsugar.nim new file mode 100644 index 0000000000000..eb19dea42423c --- /dev/null +++ b/tests/stdlib/tsugar.nim @@ -0,0 +1,21 @@ +discard """ + file: "tsugar.nim" + output: "" +""" +import sugar +import macros + +block extractGeneric: + type Foo[T1, T2]=object + type Foo2=Foo[float, string] + doAssert extractGeneric(Foo[float, string], 1) is string + doAssert extractGeneric(Foo2, 1) is string + # workaround for seq[int].T not working, + # see https://github.com/nim-lang/Nim/issues/8433 + doAssert extractGeneric(seq[int], 0) is int + doAssert extractGeneric(seq[seq[string]], 0) is seq[string] + doAssert: not compiles(extractGeneric(seq[int], 1)) + doAssert extractGeneric(seq[int], -1) is seq + + type Foo3[T] = T + doAssert extractGeneric(Foo3[int], 0) is int