diff options
Diffstat (limited to 'lib/core/macros.nim')
-rw-r--r-- | lib/core/macros.nim | 121 |
1 files changed, 96 insertions, 25 deletions
diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 8c70d2b47..b08a2198e 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -21,7 +21,7 @@ type nnkInt16Lit, nnkInt32Lit, nnkInt64Lit, nnkUIntLit, nnkUInt8Lit, nnkUInt16Lit, nnkUInt32Lit, nnkUInt64Lit, nnkFloatLit, nnkFloat32Lit, nnkFloat64Lit, nnkFloat128Lit, nnkStrLit, nnkRStrLit, - nnkTripleStrLit, nnkNilLit, nnkMetaNode, nnkDotCall, + nnkTripleStrLit, nnkNilLit, nnkComesFrom, nnkDotCall, nnkCommand, nnkCall, nnkCallStrLit, nnkInfix, nnkPrefix, nnkPostfix, nnkHiddenCallConv, nnkExprEqExpr, @@ -113,7 +113,9 @@ type type NimIdent* = object of RootObj - ## represents a Nim identifier in the AST + ## represents a Nim identifier in the AST. **Note**: This is only + ## rarely useful, for identifier construction from a string + ## use ``ident"abc"``. NimSymObj = object # hidden NimSym* = ref NimSymObj @@ -129,14 +131,11 @@ const nnkCallKinds* = {nnkCall, nnkInfix, nnkPrefix, nnkPostfix, nnkCommand, nnkCallStrLit} -proc `[]`*(n: NimNode, i: int): NimNode {.magic: "NChild", noSideEffect.} - ## get `n`'s `i`'th child. - -proc `[]=`*(n: NimNode, i: int, child: NimNode) {.magic: "NSetChild", - noSideEffect.} - ## set `n`'s `i`'th child to `child`. +proc `!`*(s: string): NimIdent {.magic: "StrToIdent", noSideEffect, deprecated.} + ## constructs an identifier from the string `s` + ## **Deprecated since version 0.18.0**: Use ``toNimIdent`` instead. -proc `!`*(s: string): NimIdent {.magic: "StrToIdent", noSideEffect.} +proc toNimIdent*(s: string): NimIdent {.magic: "StrToIdent", noSideEffect.} ## constructs an identifier from the string `s` proc `$`*(i: NimIdent): string {.magic: "IdentToStr", noSideEffect.} @@ -162,6 +161,20 @@ proc sameType*(a, b: NimNode): bool {.magic: "SameNodeType", noSideEffect.} = proc len*(n: NimNode): int {.magic: "NLen", noSideEffect.} ## returns the number of children of `n`. +proc `[]`*(n: NimNode, i: int): NimNode {.magic: "NChild", noSideEffect.} + ## get `n`'s `i`'th child. + +proc `[]`*(n: NimNode, i: BackwardsIndex): NimNode = n[n.len - i.int] + ## get `n`'s `i`'th child. + +proc `[]=`*(n: NimNode, i: int, child: NimNode) {.magic: "NSetChild", + noSideEffect.} + ## set `n`'s `i`'th child to `child`. + +proc `[]=`*(n: NimNode, i: BackwardsIndex, child: NimNode) = + ## set `n`'s `i`'th child to `child`. + n[n.len - i.int] = child + proc add*(father, child: NimNode): NimNode {.magic: "NAdd", discardable, noSideEffect, locks: 0.} ## Adds the `child` to the `father` node. Returns the @@ -230,7 +243,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. ## @@ -283,7 +296,7 @@ proc newIdentNode*(i: NimIdent): NimNode {.compileTime.} = proc newIdentNode*(i: string): NimNode {.compileTime.} = ## creates an identifier node from `i` result = newNimNode(nnkIdent) - result.ident = !i + result.ident = toNimIdent i type @@ -393,7 +406,7 @@ proc quote*(bl: typed, op = "``"): NimNode {.magic: "QuoteAst", noSideEffect.} ## ## .. code-block:: nim ## - ## macro check(ex: expr): stmt = + ## macro check(ex: untyped): typed = ## # this is a simplified version of the check macro from the ## # unittest module. ## @@ -428,6 +441,13 @@ proc expectLen*(n: NimNode, len: int) {.compileTime.} = ## macros that check its number of arguments. if n.len != len: error("macro expects a node with " & $len & " children", n) +proc expectLen*(n: NimNode, min, max: int) {.compileTime.} = + ## checks that `n` has a number of children in the range ``min..max``. + ## If this is not the case, compilation aborts with an error message. + ## This is useful for writing macros that check its number of arguments. + if n.len < min or n.len > max: + error("macro expects a node with " & $min & ".." & $max " children", n) + proc newTree*(kind: NimNodeKind, children: varargs[NimNode]): NimNode {.compileTime.} = ## produces a new node with children. @@ -463,7 +483,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) @@ -567,7 +586,7 @@ proc newLit*[T](arg: seq[T]): NimNode {.compileTime.} = proc newLit*(arg: tuple): NimNode {.compileTime.} = result = nnkPar.newTree for a,b in arg.fieldPairs: - result.add nnkExprColonExpr.newTree( newIdentNode(a), newLit(b) ) + result.add nnkExprColonExpr.newTree(newIdentNode(a), newLit(b)) proc newLit*(s: string): NimNode {.compileTime.} = ## produces a new string literal node. @@ -601,7 +620,7 @@ proc treeRepr*(n: NimNode): string {.compileTime, benign.} = of nnkCharLit..nnkInt64Lit: res.add(" " & $n.intVal) of nnkFloatLit..nnkFloat64Lit: res.add(" " & $n.floatVal) of nnkStrLit..nnkTripleStrLit: res.add(" " & $n.strVal) - of nnkIdent: res.add(" !\"" & $n.ident & '"') + of nnkIdent: res.add(" ident\"" & $n.ident & '"') of nnkSym: res.add(" \"" & $n.symbol & '"') of nnkNone: assert false else: @@ -626,7 +645,7 @@ proc lispRepr*(n: NimNode): string {.compileTime, benign.} = of nnkCharLit..nnkInt64Lit: add(result, $n.intVal) of nnkFloatLit..nnkFloat64Lit: add(result, $n.floatVal) of nnkStrLit..nnkTripleStrLit: add(result, $n.strVal) - of nnkIdent: add(result, "!\"" & $n.ident & '"') + of nnkIdent: add(result, "ident\"" & $n.ident & '"') of nnkSym: add(result, $n.symbol) of nnkNone: assert false else: @@ -650,7 +669,7 @@ proc astGenRepr*(n: NimNode): string {.compileTime, benign.} = ## .. code-block:: nim ## nnkStmtList.newTree( ## nnkCommand.newTree( - ## newIdentNode(!"echo"), + ## newIdentNode("echo"), ## newLit("Hello world") ## ) ## ) @@ -704,7 +723,7 @@ proc astGenRepr*(n: NimNode): string {.compileTime, benign.} = of nnkIntLit..nnkInt64Lit: res.add($n.intVal) of nnkFloatLit..nnkFloat64Lit: res.add($n.floatVal) of nnkStrLit..nnkTripleStrLit: res.add($n.strVal.escape()) - of nnkIdent: res.add("!" & ($n.ident).escape()) + of nnkIdent: res.add(($n.ident).escape()) of nnkSym: res.add(($n.symbol).escape()) of nnkNone: assert false else: @@ -839,7 +858,7 @@ proc newNilLit*(): NimNode {.compileTime.} = ## New nil literal shortcut result = newNimNode(nnkNilLit) -proc last*(node: NimNode): NimNode {.compileTime.} = node[<node.len] +proc last*(node: NimNode): NimNode {.compileTime.} = node[node.len-1] ## Return the last item in nodes children. Same as `node[^1]` @@ -884,10 +903,52 @@ proc newIfStmt*(branches: varargs[tuple[cond, body: NimNode]]): for i in branches: result.add(newNimNode(nnkElifBranch).add(i.cond, i.body)) +proc newEnum*(name: NimNode, fields: openArray[NimNode], + public, pure: bool): NimNode {.compileTime.} = + + ## Creates a new enum. `name` must be an ident. Fields are allowed to be + ## either idents or EnumFieldDef + ## + ## .. code-block:: nim + ## + ## newEnum( + ## name = ident("Colors"), + ## fields = [ident("Blue"), ident("Red")], + ## public = true, pure = false) + ## + ## # type Colors* = Blue Red + ## + + expectKind name, nnkIdent + doAssert len(fields) > 0, "Enum must contain at least one field" + for field in fields: + expectKind field, {nnkIdent, nnkEnumFieldDef} + + let enumBody = newNimNode(nnkEnumTy).add(newEmptyNode()).add(fields) + var typeDefArgs = [name, newEmptyNode(), enumBody] + + if public: + let postNode = newNimNode(nnkPostfix).add( + newIdentNode("*"), typeDefArgs[0]) + + typeDefArgs[0] = postNode + + if pure: + let pragmaNode = newNimNode(nnkPragmaExpr).add( + typeDefArgs[0], + add(newNimNode(nnkPragma), newIdentNode("pure"))) + + typeDefArgs[0] = pragmaNode + + let + typeDef = add(newNimNode(nnkTypeDef), typeDefArgs) + typeSect = add(newNimNode(nnkTypeSection), typeDef) + + return typeSect proc copyChildrenTo*(src, dest: NimNode) {.compileTime.}= ## Copy all children from `src` to `dest` - for i in 0 .. < src.len: + for i in 0 ..< src.len: dest.add src[i].copyNimTree template expectRoutine(node: NimNode) = @@ -986,6 +1047,11 @@ iterator items*(n: NimNode): NimNode {.inline.} = for i in 0 ..< n.len: yield n[i] +iterator pairs*(n: NimNode): (int, NimNode) {.inline.} = + ## Iterates over the children of the NimNode ``n`` and its indices. + for i in 0 ..< n.len: + yield (i, n[i]) + iterator children*(n: NimNode): NimNode {.inline.} = ## Iterates over the children of the NimNode ``n``. for i in 0 ..< n.len: @@ -996,7 +1062,7 @@ template findChild*(n: NimNode; cond: untyped): NimNode {.dirty.} = ## ## .. code-block:: nim ## var res = findChild(n, it.kind == nnkPostfix and - ## it.basename.ident == !"foo") + ## it.basename.ident == toNimIdent"foo") block: var res: NimNode for it in n.children: @@ -1030,7 +1096,7 @@ proc basename*(a: NimNode): NimNode = proc `basename=`*(a: NimNode; val: string) {.compileTime.}= case a.kind - of nnkIdent: macros.`ident=`(a, !val) + of nnkIdent: macros.`ident=`(a, toNimIdent val) of nnkPostfix, nnkPrefix: a[1] = ident(val) else: quit "Do not know how to get basename of (" & treeRepr(a) & ")\n" & repr(a) @@ -1091,7 +1157,7 @@ proc eqIdent*(node: NimNode; s: string): bool {.compileTime.} = ## other ways like ``node.ident`` are much more error-prone, unfortunately. case node.kind of nnkIdent: - result = node.ident == !s + result = node.ident == toNimIdent s of nnkSym: result = eqIdent($node.symbol, s) of nnkOpenSymChoice, nnkClosedSymChoice: @@ -1099,10 +1165,10 @@ proc eqIdent*(node: NimNode; s: string): bool {.compileTime.} = else: result = false -proc hasArgOfName* (params: NimNode; name: string): bool {.compiletime.}= +proc hasArgOfName*(params: NimNode; name: string): bool {.compiletime.}= ## Search nnkFormalParams for an argument. assert params.kind == nnkFormalParams - for i in 1 .. <params.len: + for i in 1 ..< params.len: template node: untyped = params[i] if name.eqIdent( $ node[0]): return true @@ -1160,3 +1226,8 @@ when not defined(booting): macro payload: untyped {.gensym.} = result = parseStmt(e) payload() + +macro unpackVarargs*(callee: untyped; args: varargs[untyped]): untyped = + result = newCall(callee) + for i in 0 ..< args.len: + result.add args[i] |