diff options
author | Andreas Rumpf <rumpf_a@web.de> | 2017-09-15 09:27:51 +0200 |
---|---|---|
committer | Andreas Rumpf <rumpf_a@web.de> | 2017-09-15 09:27:51 +0200 |
commit | 39f0195ebf30cc9f4b96953423298d7a2130eac8 (patch) | |
tree | bb4daf580c239b59d232e186814b69fc642ce6d5 /lib/core/macros.nim | |
parent | bc738d63a728ee6030cc224c8808990a6f641feb (diff) | |
parent | 2ef65d5cdf3d65cbfab4c39796ddb4e70e9ebb37 (diff) | |
download | Nim-39f0195ebf30cc9f4b96953423298d7a2130eac8.tar.gz |
Merge branch 'devel' into araq
Diffstat (limited to 'lib/core/macros.nim')
-rw-r--r-- | lib/core/macros.nim | 150 |
1 files changed, 140 insertions, 10 deletions
diff --git a/lib/core/macros.nim b/lib/core/macros.nim index af1e9de28..3cfefb5c1 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -94,8 +94,9 @@ type ntyVarargs, ntyUnused, ntyError, - ntyBuiltinTypeClass, ntyConcept, ntyConceptInst, ntyComposite, - ntyAnd, ntyOr, ntyNot + ntyBuiltinTypeClass, ntyUserTypeClass, ntyUserTypeClassInst, + ntyCompositeTypeClass, ntyInferred, ntyAnd, ntyOr, ntyNot, + ntyAnything, ntyStatic, ntyFromExpr, ntyFieldAccessor, ntyVoid TNimTypeKinds* {.deprecated.} = set[NimTypeKind] NimSymKind* = enum @@ -149,6 +150,9 @@ proc `==`*(a, b: NimIdent): bool {.magic: "EqIdent", noSideEffect.} proc `==`*(a, b: NimNode): bool {.magic: "EqNimrodNode", noSideEffect.} ## compares two Nim nodes +proc `==`*(a, b: NimSym): bool {.magic: "EqNimrodNode", noSideEffect.} + ## compares two Nim symbols + proc sameType*(a, b: NimNode): bool {.magic: "SameNodeType", noSideEffect.} = ## compares two Nim nodes' types. Return true if the types are the same, ## eg. true when comparing alias with original type. @@ -317,10 +321,30 @@ proc toStrLit*(n: NimNode): NimNode {.compileTime.} = ## in a string literal node return newStrLitNode(repr(n)) -proc lineinfo*(n: NimNode): string {.magic: "NLineInfo", noSideEffect.} +type + LineInfo* = object + filename*: string + line*,column*: int + +proc `$`*(arg: Lineinfo): string = + result = arg.filename & "(" & $arg.line & ", " & $arg.column & ")" + +#proc lineinfo*(n: NimNode): LineInfo {.magic: "NLineInfo", noSideEffect.} ## returns the position the node appears in the original source file ## in the form filename(line, col) +proc getLine(arg: NimNode): int {.magic: "NLineInfo", noSideEffect.} +proc getColumn(arg: NimNode): int {.magic: "NLineInfo", noSideEffect.} +proc getFile(arg: NimNode): string {.magic: "NLineInfo", noSideEffect.} + +proc lineInfoObj*(n: NimNode): LineInfo {.compileTime.} = + result.filename = n.getFile + result.line = n.getLine + result.column = n.getColumn + +proc lineInfo*(arg: NimNode): string {.compileTime.} = + $arg.lineInfoObj + proc internalParseExpr(s: string): NimNode {. magic: "ParseExprToAst", noSideEffect.} @@ -527,10 +551,17 @@ proc newLit*[N,T](arg: array[N,T]): NimNode {.compileTime.} = result.add newLit(x) proc newLit*[T](arg: seq[T]): NimNode {.compileTime.} = - result = nnkBracket.newTree + var bracket = nnkBracket.newTree for x in arg: - result.add newLit(x) - result = nnkPrefix.newTree(bindSym"@", result) + bracket.add newLit(x) + + result = nnkCall.newTree( + nnkBracketExpr.newTree( + nnkAccQuoted.newTree( bindSym"@" ), + getTypeInst( bindSym"T" ) + ), + bracket + ) proc newLit*(arg: tuple): NimNode {.compileTime.} = result = nnkPar.newTree @@ -557,7 +588,8 @@ proc nestList*(theProc: NimIdent, proc treeRepr*(n: NimNode): string {.compileTime, benign.} = ## Convert the AST `n` to a human-readable tree-like string. ## - ## See also `repr` and `lispRepr`. + ## See also `repr`, `lispRepr`, and `astGenRepr`. + proc traverse(res: var string, level: int, n: NimNode) {.benign.} = for i in 0..level-1: res.add " " res.add(($n.kind).substr(3)) @@ -582,7 +614,7 @@ proc treeRepr*(n: NimNode): string {.compileTime, benign.} = proc lispRepr*(n: NimNode): string {.compileTime, benign.} = ## Convert the AST `n` to a human-readable lisp-like string, ## - ## See also `repr` and `treeRepr`. + ## See also `repr`, `treeRepr`, and `astGenRepr`. result = ($n.kind).substr(3) add(result, "(") @@ -605,9 +637,96 @@ proc lispRepr*(n: NimNode): string {.compileTime, benign.} = add(result, ")") +proc astGenRepr*(n: NimNode): string {.compileTime, benign.} = + ## Convert the AST `n` to the code required to generate that AST. So for example + ## + ## .. code-block:: nim + ## astGenRepr: + ## echo "Hello world" + ## + ## Would output: + ## + ## .. code-block:: nim + ## nnkStmtList.newTree( + ## nnkCommand.newTree( + ## newIdentNode(!"echo"), + ## newLit("Hello world") + ## ) + ## ) + ## + ## See also `repr`, `treeRepr`, and `lispRepr`. + + const + NodeKinds = {nnkEmpty, nnkNilLit, nnkIdent, nnkSym, nnkNone} + LitKinds = {nnkCharLit..nnkInt64Lit, nnkFloatLit..nnkFloat64Lit, nnkStrLit..nnkTripleStrLit} + + proc escape(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect.} = + ## Functions copied from strutils + proc toHex(x: BiggestInt, len: Positive): string {.noSideEffect, rtl.} = + const + HexChars = "0123456789ABCDEF" + var + t = x + result = newString(len) + for j in countdown(len-1, 0): + result[j] = HexChars[t and 0xF] + t = t shr 4 + # handle negative overflow + if t == 0 and x < 0: t = -1 + + result = newStringOfCap(s.len + s.len shr 2) + result.add(prefix) + for c in items(s): + case c + of '\0'..'\31', '\128'..'\255': + add(result, "\\x") + add(result, toHex(ord(c), 2)) + of '\\': add(result, "\\\\") + of '\'': add(result, "\\'") + of '\"': add(result, "\\\"") + else: add(result, c) + add(result, suffix) + + proc traverse(res: var string, level: int, n: NimNode) {.benign.} = + for i in 0..level-1: res.add " " + if n.kind in NodeKinds: + res.add("new" & ($n.kind).substr(3) & "Node(") + elif n.kind in LitKinds: + res.add("newLit(") + else: + res.add($n.kind) + + case n.kind + of nnkEmpty: discard + of nnkNilLit: res.add("nil") + of nnkCharLit: res.add("'" & $chr(n.intVal) & "'") + 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 nnkSym: res.add(($n.symbol).escape()) + of nnkNone: assert false + else: + res.add(".newTree(") + for j in 0..<n.len: + res.add "\n" + traverse(res, level + 1, n[j]) + if j != n.len-1: + res.add(",") + + res.add("\n") + for i in 0..level-1: res.add " " + res.add(")") + + if n.kind in NodeKinds+LitKinds: + res.add(")") + + result = "" + traverse(result, 0, n) + macro dumpTree*(s: untyped): untyped = echo s.treeRepr ## Accepts a block of nim code and prints the parsed abstract syntax - ## tree using the `toTree` function. Printing is done *at compile time*. + ## tree using the `treeRepr` function. Printing is done *at compile time*. ## ## You can use this as a tool to explore the Nim's abstract syntax ## tree and to discover what kind of nodes must be created to represent @@ -615,7 +734,16 @@ macro dumpTree*(s: untyped): untyped = echo s.treeRepr macro dumpLisp*(s: untyped): untyped = echo s.lispRepr ## Accepts a block of nim code and prints the parsed abstract syntax - ## tree using the `toLisp` function. Printing is done *at compile time*. + ## tree using the `lispRepr` function. Printing is done *at compile time*. + ## + ## See `dumpTree`. + +macro dumpAstGen*(s: untyped): untyped = echo s.astGenRepr + ## Accepts a block of nim code and prints the parsed abstract syntax + ## tree using the `astGenRepr` function. Printing is done *at compile time*. + ## + ## You can use this as a tool to write macros quicker by writing example + ## outputs and then copying the snippets into the macro for modification. ## ## See `dumpTree`. @@ -766,6 +894,8 @@ template expectRoutine(node: NimNode) = proc name*(someProc: NimNode): NimNode {.compileTime.} = someProc.expectRoutine result = someProc[0] + if result.kind == nnkPostfix: + result = result[1] proc `name=`*(someProc: NimNode; val: NimNode) {.compileTime.} = someProc.expectRoutine someProc[0] = val |