tunique_type.nim 2.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768
  1. # Bug #2022
  2. discard """
  3. output: '''@[97, 45]
  4. @[true, false]
  5. @[false, false]'''
  6. """
  7. ## The goal of this snippet is to provide and test a construct for general-
  8. ## purpose, random-access mapping. I use an AST-manipulation-based approach
  9. ## because it's more efficient than using procedure pointers and less
  10. ## verbose than defining a new callable type for every invocation of `map`.
  11. import sugar
  12. import macros
  13. import strutils
  14. #===============================================================================
  15. # Define a system for storing copies of ASTs as static strings.
  16. # This serves the same purpose as D's `alias` parameters for types, used heavily
  17. # in its popular `ranges` and `algorithm` modules.
  18. var exprNodes {.compileTime.} = newSeq[NimNode]()
  19. proc refExpr(exprNode: NimNode): string {.compileTime.} =
  20. exprNodes.add exprNode.copy
  21. "expr" & $(exprNodes.len - 1)
  22. proc derefExpr(exprRef: string): NimNode {.compileTime.} =
  23. exprNodes[parseInt(exprRef[4 .. ^1])]
  24. #===============================================================================
  25. # Define a type that allows a callable expression to be mapped onto elements
  26. # of an indexable collection.
  27. type Mapped[Input; predicate: static[string]] = object
  28. input: Input
  29. macro map(input, predicate: untyped): untyped =
  30. let predicate = callsite()[2]
  31. newNimNode(nnkObjConstr).add(
  32. newNimNode(nnkBracketExpr).add(
  33. ident"Mapped",
  34. newNimNode(nnkTypeOfExpr).add(input),
  35. newLit(refExpr(predicate))),
  36. newNimNode(nnkExprColonExpr).add(
  37. ident"input", input))
  38. proc `[]`(m: Mapped, i: int): auto =
  39. macro buildResult: untyped =
  40. newCall(
  41. derefExpr(m.predicate),
  42. newNimNode(nnkBracketExpr).add(
  43. newDotExpr(ident"m", ident"input"),
  44. ident"i"))
  45. buildResult()
  46. #===============================================================================
  47. # Test out our generic mapping construct.
  48. let a = "a-string".map(ord)
  49. let b = @["a", "seq"].map((e: string) => e == "a")
  50. let c = "another-string".map((e: char) => e == 'o')
  51. echo(@[a[0], a[1]]) # @[97, 45]
  52. echo(@[b[0], b[1]]) # @[true, false]
  53. echo(@[c[0], c[1]]) # @[false, false]