summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorAndreas Rumpf <rumpf_a@web.de>2017-11-18 14:14:08 +0100
committerAndreas Rumpf <rumpf_a@web.de>2017-11-18 14:18:00 +0100
commit908a25a2ca262b212ab1b9e4dc4da42ece46b6df (patch)
treee11d744bd14dfdcc0cc3ee8f986fb45cfe534dee
parent5b57abe3544261ba2df29648d48a997e8d1983da (diff)
downloadNim-908a25a2ca262b212ab1b9e4dc4da42ece46b6df.tar.gz
replaced asArray with a much more powerful mapLiterals macro
-rw-r--r--changelog.md5
-rw-r--r--lib/core/macros.nim8
-rw-r--r--lib/pure/collections/sequtils.nim55
3 files changed, 49 insertions, 19 deletions
diff --git a/changelog.md b/changelog.md
index 6244e4f09..2e0780220 100644
--- a/changelog.md
+++ b/changelog.md
@@ -78,10 +78,13 @@ This now needs to be written as:
   via the new ``nim doc0`` command.
 - Added ``system.getStackTraceEntries`` that allows you to access the stack
   trace in a structured manner without string parsing.
+- Added ``sequtils.mapLiterals`` for easier construction of array and tuple
+  literals.
+- Added ``macros.isAtomicLit`` predicate.
 - Moved from stdlib into Nimble packages:
   - [``basic2d``](https://github.com/nim-lang/basic2d)
     _deprecated: use ``glm``, ``arraymancer``, ``neo``, or another package instead_
-  - [``basic3d``](https://github.com/nim-lang/basic3d) 
+  - [``basic3d``](https://github.com/nim-lang/basic3d)
     _deprecated: use ``glm``, ``arraymancer``, ``neo``, or another package instead_
   - [``gentabs``](https://github.com/lcrees/gentabs)
   - [``libuv``](https://github.com/lcrees/libuv)
diff --git a/lib/core/macros.nim b/lib/core/macros.nim
index 5d0cb0c0d..e51b79abd 100644
--- a/lib/core/macros.nim
+++ b/lib/core/macros.nim
@@ -237,7 +237,7 @@ proc `ident=`*(n: NimNode, val: NimIdent) {.magic: "NSetIdent", noSideEffect.}
 proc `strVal=`*(n: NimNode, val: string) {.magic: "NSetStrVal", noSideEffect.}
 
 proc newNimNode*(kind: NimNodeKind,
-                 lineInfoFrom: NimNode=nil): NimNode
+                 lineInfoFrom: NimNode = nil): NimNode
   {.magic: "NNewNimNode", noSideEffect.}
   ## Creates a new AST node of the specified kind.
   ##
@@ -470,7 +470,6 @@ proc newLit*(c: char): NimNode {.compileTime.} =
   result = newNimNode(nnkCharLit)
   result.intVal = ord(c)
 
-
 proc newLit*(i: int): NimNode {.compileTime.} =
   ## produces a new integer literal node.
   result = newNimNode(nnkIntLit)
@@ -581,6 +580,11 @@ proc newLit*(s: string): NimNode {.compileTime.} =
   result = newNimNode(nnkStrLit)
   result.strVal = s
 
+proc isAtomicLit*(n: NimNode): bool =
+  ## returns true if ``n`` is some kind literal like ``0.3`` (a ``float``
+  ## literal) or ``"abc"`` (a ``string`` literal).
+  result = n.kind in {nnkCharLit..nnkNilLit}
+
 proc nestList*(theProc: NimIdent,
                x: NimNode): NimNode {.compileTime.} =
   ## nests the list `x` into a tree of call expressions:
diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim
index 8bbfb5f55..0088e42d1 100644
--- a/lib/pure/collections/sequtils.nim
+++ b/lib/pure/collections/sequtils.nim
@@ -706,27 +706,50 @@ template newSeqWith*(len: int, init: untyped): untyped =
     result[i] = init
   result
 
-macro asArray*(targetType: typedesc, values: typed): untyped =
-  ## applies a type conversion to each of the elements in the specified
-  ## array literal. Each element is converted to the ``targetType`` type..
+proc mapLitsImpl(constructor: NimNode; op: NimNode; nested: bool): NimNode =
+  if isAtomicLit(constructor):
+    result = newNimNode(nnkCall, lineInfoFrom=constructor)
+    result.add op
+    result.add constructor
+  else:
+    result = newNimNode(constructor.kind, lineInfoFrom=constructor)
+    for v in constructor:
+      if nested or isAtomicLit(v):
+        result.add mapLitsImpl(v, op, nested)
+      else:
+        result.add v
+
+macro mapLiterals*(constructor, op: untyped;
+                   nested = true): untyped =
+  ## applies ``op`` to each of the **atomic** literals like ``3``
+  ## or ``"abc"`` in the specified ``constructor`` AST. This can
+  ## be used to map every array element to some target type:
   ##
   ## Example:
   ##
   ## .. code-block::
-  ##   let x = asArray(int, [0.1, 1.2, 2.3, 3.4])
+  ##   let x = mapLiterals([0.1, 1.2, 2.3, 3.4], int)
   ##   doAssert x is array[4, int]
   ##
   ## Short notation for:
   ##
   ## .. code-block::
-  ##   let x = [(0.1).int, (1.2).int, (2.3).int, (3.4).int]
-  values.expectKind(nnkBracket)
-  result = newNimNode(nnkBracket, lineInfoFrom=values)
-  for i in 0 ..< len(values):
-    var call = newNimNode(nnkCall, lineInfoFrom=values[i])
-    call.add targetType
-    call.add values[i]
-    result.add call
+  ##   let x = [int(0.1), int(1.2), int(2.3), int(3.4)]
+  ##
+  ## If ``nested`` is true, the literals are replaced everywhere
+  ## in the ``constructor`` AST, otherwise only the first level
+  ## is considered:
+  ##
+  ## .. code-block::
+  ##   mapLiterals((1, ("abc"), 2), float, nested=false)
+  ##
+  ## Produces::
+  ##
+  ##   (float(1), ("abc"), float(2))
+  ##
+  ## There are no constraints for the ``constructor`` AST, it
+  ## works for nested tuples of arrays of sets etc.
+  result = mapLitsImpl(constructor, op, nested.boolVal)
 
 when isMainModule:
   import strutils
@@ -1016,11 +1039,11 @@ when isMainModule:
     seq2D[0][1] = true
     doAssert seq2D == @[@[true, true], @[true, false], @[false, false], @[false, false]]
 
-  block: # asArray tests
-    let x = asArray(int, [1.2, 2.3, 3.4, 4.5])
+  block: # mapLiterals tests
+    let x = mapLiterals([0.1, 1.2, 2.3, 3.4], int)
     doAssert x is array[4, int]
-    let y = asArray(`$`, [1.2, 2.3, 3.4, 4.5])
-    doAssert y is array[4, string]
+    doAssert mapLiterals((1, ("abc"), 2), float, nested=false) == (float(1), "abc", float(2))
+    doAssert mapLiterals(([1], ("abc"), 2), `$`, nested=true) == (["1"], "abc", "2")
 
   when not defined(testing):
     echo "Finished doc tests"