# # # Nim's Runtime Library # (c) Copyright 2015 Dominik Picheta # # See the file "copying.txt", included in this # distribution, for details about the copyright. # ## This module implements nice syntactic sugar based on Nim's ## macro system. import macros proc createProcType(p, b: NimNode): NimNode {.compileTime.} = #echo treeRepr(p) #echo treeRepr(b) result = newNimNode(nnkProcTy) var formalParams = newNimNode(nnkFormalParams) formalParams.add b case p.kind of nnkPar, nnkTupleConstr: for i in 0 ..< p.len: let ident = p[i] var identDefs = newNimNode(nnkIdentDefs) case ident.kind of nnkExprColonExpr: identDefs.add ident[0] identDefs.add ident[1] else: identDefs.add newIdentNode("i" & $i) identDefs.add(ident) identDefs.add newEmptyNode() formalParams.add identDefs else: var identDefs = newNimNode(nnkIdentDefs) identDefs.add newIdentNode("i0") identDefs.add(p) identDefs.add newEmptyNode() formalParams.add identDefs result.add formalParams result.add newEmptyNode() #echo(treeRepr(result)) #echo(result.toStrLit()) macro `=>`*(p, b: untyped): untyped = ## Syntax sugar for anonymous procedures. ## ## .. code-block:: nim ## ## proc passTwoAndTwo(f: (int, int) -> int): int = ## f(2, 2) ## ## passTwoAndTwo((x, y) => x + y) # 4 #echo treeRepr(p) #echo(treeRepr(b)) var params: seq[NimNode] = @[newIdentNode("auto")] case p.kind of nnkPar, nnkTupleConstr: for c in children(p): var identDefs = newNimNode(nnkIdentDefs) case c.kind of nnkExprColonExpr: identDefs.add(c[0]) identDefs.add(c[1]) identDefs.add(newEmptyNode()) of nnkIdent: identDefs.add(c) identDefs.add(newIdentNode("auto")) identDefs.add(newEmptyNode()) of nnkInfix: if c[0].kind == nnkIdent and c[0].ident == !"->": var procTy = createProcType(c[1], c[2]) params[0] = procTy[0][0] for i in 1 ..< procTy[0].len: params.add(procTy[0][i]) else: error("Expected proc type (->) got (" & $c[0].ident & ").") break else: echo treeRepr c error("Incorrect procedure parameter list.") params.add(identDefs) of nnkIdent: var identDefs = newNimNode(nnkIdentDefs) identDefs.add(p) identDefs.add(newIdentNode("auto")) identDefs.add(newEmptyNode()) params.add(identDefs) of nnkInfix: if p[0].kind == nnkIdent and p[0].ident == !"->": var procTy = createProcType(p[1], p[2]) params[0] = procTy[0][0] for i in 1 ..< procTy[0].len: params.add(procTy[0][i]) else: error("Expected proc type (->) got (" & $p[0].ident & ").") else: error("Incorrect procedure parameter list.") result = newProc(params = params, body = b, procType = nnkLambda) #echo(result.treeRepr) #echo(result.toStrLit()) #return result # TODO: Bug? macro `->`*(p, b: untyped): untyped = ## Syntax sugar for procedure types. ## ## .. code-block:: nim ## ## proc pass2(f: (float, float) -> float): float = ## f(2, 2) ## ## # is the same as: ## ## proc pass2(f: proc (x, y: float): float): float = ## f(2, 2) result = createProcType(p, b) type ListComprehension = object var lc* {.deprecated.}: ListComprehension template `|`*(lc: ListComprehension, comp: untyped): untyped {.deprecated.} = lc macro `[]`*(lc: ListComprehension, comp, typ: untyped): untyped {.deprecated.} = ## List comprehension, returns a sequence. `comp` is the actual list ## comprehension, for example ``x | (x <- 1..10, x mod 2 == 0)``. `typ` is ## the type that will be stored inside the result seq. ## ## .. code-block:: nim ## ## echo lc[x | (x <- 1..10, x mod 2 == 0), int] ## ## const n = 20 ## echo lc[(x,y,z) | (x <- 1..n, y <- x..n, z <- y..n, x*x + y*y == z*z), ## tuple[a,b,c: int]] ## **Deprecated since version 0.19.9** expectLen(comp, 3) expectKind(comp, nnkInfix) assert($comp[0] == "|") result = newCall( newDotExpr( newIdentNode("result"), newIdentNode("add")), comp[1]) for i in countdown(comp[2].len-1, 0): let x = comp[2][i] expectMinLen(x, 1) if x[0].kind == nnkIdent and $x[0].ident == "<-": expectLen(x, 3) result = newNimNode(nnkForStmt).add(x[1], x[2], result) else: result = newIfStmt((x, result)) result = newNimNode(nnkCall).add( newNimNode(nnkPar).add( newNimNode(nnkLambda).add( newEmptyNode(), newEmptyNode(), newEmptyNode(), newNimNode(nnkFormalParams).add( newNimNode(nnkBracketExpr).add( newIdentNode("seq"), typ)), newEmptyNode(), newEmptyNode(), newStmtList( newAssignment( newIdentNode("result"), newNimNode(nnkPrefix).add( newIdentNode("@"), newNimNode(nnkBracket))), result)))) macro dump*(x: typed): untyped = ## Dumps the content of an expression, useful for debugging. ## It accepts any expression and prints a textual representation ## of the tree representing the expression - as it would appear in ## source code - together with the value of the expression. ## ## As an example, ## ## .. code-block:: nim ## let ## x = 10 ## y = 20 ## dump(x + y) ## ## will print ``x + y = 30``. let s = x.toStrLit let r = quote do: debugEcho `s`, " = ", `x` return r # TODO: consider exporting this in macros.nim proc freshIdentNodes(ast: NimNode): NimNode = # Replace NimIdent and NimSym by a fresh ident node # see also https://github.com/nim-lang/Nim/pull/8531#issuecomment-410436458 proc inspect(node: NimNode): NimNode = case node.kind: of nnkIdent, nnkSym: result = ident($node) of nnkEmpty, nnkLiterals: result = node else: result = node.kind.newTree() for child in node: result.add inspect(child) result = inspect(ast) macro distinctBase*(T: typedesc): untyped = ## reverses ``type T = distinct A``; works recursively. runnableExamples: type T = distinct int doAssert distinctBase(T) is int doAssert: not compiles(distinctBase(int)) type T2 = distinct T doAssert distinctBase(T2) is int let typeNode = getTypeImpl(T) expectKind(typeNode, nnkBracketExpr) if typeNode[0].typeKind != ntyTypeDesc: error "expected typeDesc, got " & $typeNode[0] var typeSym = typeNode[1] typeSym = getTypeImpl(typeSym) if typeSym.typeKind != ntyDistinct: error "type is not distinct" typeSym = typeSym[0] while typeSym.typeKind == ntyDistinct: typeSym = getTypeImpl(typeSym)[0] typeSym.freshIdentNodes