Merge pull request #444 from fowlmouth/patch-6

add macro_dsl api
This commit is contained in:
Araq
2013-05-19 17:19:31 -07:00

View File

@@ -112,7 +112,7 @@ proc `[]`*(n: PNimrodNode, i: int): PNimrodNode {.magic: "NChild".}
proc `[]=`*(n: PNimrodNode, i: int, child: PNimrodNode) {.magic: "NSetChild".}
## set `n`'s `i`'th child to `child`.
proc `!`*(s: string): TNimrodIdent {.magic: "StrToIdent".}
proc `!`*(s: string): TNimrodIdent {.magic: "StrToIdent", deprecated.}
## constructs an identifier from the string `s`
proc `$`*(i: TNimrodIdent): string {.magic: "IdentToStr".}
@@ -410,3 +410,240 @@ macro dumpTreeImm*(s: stmt): stmt {.immediate.} = echo s.treeRepr
macro dumpLispImm*(s: stmt): stmt {.immediate.} = echo s.lispRepr
## The ``immediate`` version of `dumpLisp`.
proc newEmptyNode*(): PNimrodNode {.compileTime, noSideEffect.} =
## Create a new empty node
result = newNimNode(nnkEmpty)
proc newStmtList*(stmts: varargs[PNimrodNode]): PNimrodNode {.compileTime.}=
## Create a new statement list
result = newNimNode(nnkStmtList).add(stmts)
proc newBlockStmt*(label: PNimrodNode; body: PNimrodNode): PNimrodNode {.compileTime.} =
## Create a new block statement with label
return newNimNode(nnkBlockStmt).add(label, body)
proc newBlockStmt*(body: PNimrodNode): PNimrodNode {.compiletime.} =
## Create a new block: stmt
return newNimNode(nnkBlockStmt).add(newEmptyNode(), body)
proc newLetStmt*(name, value: PNimrodNode): PNimrodNode{.compiletime.} =
## Create a new let stmt
return newNimNode(nnkLetSection).add(
newNimNode(nnkIdentDefs).add(name, newNimNode(nnkEmpty), value))
proc newAssignment*(lhs, rhs: PNimrodNode): PNimrodNode {.compileTime, inline.} =
return newNimNode(nnkAsgn).add(lhs, rhs)
proc newDotExpr* (a, b: PNimrodNode): PNimrodNode {.compileTime, inline.} =
## Create new dot expression
## a.dot(b) -> `a.b`
return newNimNode(nnkDotExpr).add(a, b)
proc newIdentDefs*(name, kind: PNimrodNode; default = newEmptyNode()): PNimrodNode{.
compileTime.} = newNimNode(nnkIdentDefs).add(name, kind, default)
proc newNilLit*(): PNimrodNode {.compileTime.} =
## New nil literal shortcut
result = newNimNode(nnkNilLit)
proc high*(node: PNimrodNode): int {.compileTime.} = len(node) - 1
## Return the highest index available for a node
proc last*(node: PNimrodNode): PNimrodNode {.compileTime.} = node[node.high]
## Return the last item in nodes children. Same as `node[node.high()]`
const
RoutineNodes* = {
nnkProcDef, nnkMethodDef, nnkDo, nnkLambda }
AtomicNodes* = {
nnkNone .. nnkNilLit }
CallNodes* = {
nnkCall, nnkInfix, nnkPrefix, nnkPostfix, nnkCommand,
nnkCallStrLit, nnkHiddenCallConv}
from strutils import cmpIgnoreStyle, format
proc ExpectKind*(n: PNimrodNode; k: set[TNimrodNodeKind]) {.compileTime.} =
assert n.kind in k, "Expected one of $1, got $2".format(k, n.kind)
proc newProc*(name = newEmptyNode(); params: openarray[PNimrodNode] = [];
body: PNimrodNode = newStmtList(), procType = nnkProcDef): PNimrodNode {.compileTime.} =
## shortcut for creating a new proc
assert procType in RoutineNodes
result = newNimNode(procType).add(
name,
newEmptyNode(),
newEmptyNode(),
newNimNode(nnkFormalParams).add(params), ##params
newEmptyNode(), ## pragmas
newEmptyNode(),
body)
proc copyChildrenTo*(src, dest: PNimrodNode) {.compileTime.}=
## Copy all children from `src` to `dest`
for i in 0 .. < src.len:
dest.add src[i].copyNimTree
template expectRoutine(node: PNimrodNode): stmt =
expectKind(node, routineNodes)
proc name*(someProc: PNimrodNode): PNimrodNode {.compileTime.} =
someProc.expectRoutine
result = someProc[0]
proc `name=`*(someProc: PNimrodNode; val: PNimrodNode) {.compileTime.} =
someProc.expectRoutine
someProc[0] = val
proc params*(someProc: PNimrodNode): PNimrodNode {.compileTime.} =
someProc.expectRoutine
result = someProc[3]
proc `params=`* (someProc: PNimrodNode; params: PNimrodNode) {.compileTime.}=
someProc.expectRoutine
assert params.kind == nnkFormalParams
someProc[3] = params
proc pragma*(someProc: PNimrodNode): PNimrodNode {.compileTime.} =
## Get the pragma of a proc type
## These will be expanded
someProc.expectRoutine
result = someProc[4]
proc `pragma=`*(someProc: PNimrodNode; val: PNimrodNode){.compileTime.}=
## Set the pragma of a proc type
someProc.expectRoutine
assert val.kind in {nnkEmpty, nnkPragma}
someProc[4] = val
template badnodekind(k; f): stmt{.immediate.} =
assert false, "Invalid node kind $# for macros.`$2`".format(k, f)
proc body*(someProc: PNimrodNode): PNimrodNode {.compileTime.} =
case someProc.kind:
of routineNodes:
return someProc[6]
of nnkBlockStmt, nnkWhileStmt:
return someproc[1]
of nnkForStmt:
return someProc.last
else:
badNodeKind someproc.kind, "body"
proc `body=`*(someProc: PNimrodNode, val: PNimrodNode) {.compileTime.} =
case someProc.kind
of routineNodes:
someProc[6] = val
of nnkBlockStmt, nnkWhileStmt:
someProc[1] = val
of nnkForStmt:
someProc[high(someProc)] = val
else:
badNodeKind someProc.kind, "body="
proc `$`*(node: PNimrodNode): string {.compileTime.} =
## Get the string of an identifier node
case node.kind
of nnkIdent:
result = $node.ident
of nnkStrLit:
result = node.strval
else:
badNodeKind node.kind, "$"
proc ident*(name: string): PNimrodNode {.compileTime,inline.} = newIdentNode(name)
## Create a new ident node from a string
iterator children*(n: PNimrodNode): PNimrodNode {.inline.}=
for i in 0 .. high(n):
yield n[i]
template findChild*(n: PNimrodNode; cond: expr): PNimrodNode {.immediate, dirty.} =
## Find the first child node matching condition (or nil)
## var res = findChild(n, it.kind == nnkPostfix and it.basename.ident == !"foo")
block:
var result: PNimrodNode
for it in n.children:
if cond:
result = it
break
result
proc insert*(a: PNimrodNOde; pos: int; b: PNimrodNode) {.compileTime.} =
## Insert node B into A at pos
if high(a) < pos:
## add some empty nodes first
for i in high(a)..pos-2:
a.add newEmptyNode()
a.add b
else:
## push the last item onto the list again
## and shift each item down to pos up one
a.add(a[a.high])
for i in countdown(high(a) - 2, pos):
a[i + 1] = a[i]
a[pos] = b
proc basename*(a: PNimrodNode): PNimrodNode {.compiletime.} =
## Pull an identifier from prefix/postfix expressions
case a.kind
of nnkIdent: return a
of nnkPostfix, nnkPrefix: return a[1]
else:
quit "Do not know how to get basename of ("& treerepr(a) &")\n"& repr(a)
proc `basename=`*(a: PNimrodNode; val: string) {.compileTime.}=
case a.kind
of nnkIdent: macros.`ident=`(a, !val)
of nnkPostfix, nnkPrefix: a[1] = ident(val)
else:
quit "Do not know how to get basename of ("& treerepr(a)& ")\n"& repr(a)
proc postfix*(node: PNimrodNode; op: string): PNimrodNode {.
compileTime.} = newNimNode(nnkPostfix).add(ident(op), node)
proc prefix*(node: PNimrodNode; op: string): PNimrodNode {.
compileTime.} = newNimNode(nnkPrefix).add(ident(op), node)
proc infix*(a: PNimrodNode; op: string; b: PNimrodNode): PNimrodNode {.
compileTime.} = newNimNode(nnkInfix).add(ident(op), a, b)
proc unpackPostfix*(node: PNimrodNode): tuple[node: PNimrodNode; op: string] {.
compileTime.} =
node.expectKind nnkPostfix
result = (node[0], $node[1])
proc unpackPrefix*(node: PNimrodNode): tuple[node: PNimrodNode; op: string] {.
compileTime.} =
node.expectKind nnkPrefix
result = (node[0], $node[1])
proc unpackInfix*(node: PNimrodNode): tuple[left: PNimrodNode; op: string; right: PNimrodNode] {.
compileTime.} =
assert node.kind == nnkInfix
result = (node[0], $node[1], node[2])
proc copy*(node: PNimrodNode): PNimrodNode {.compileTime.} =
## An alias for copyNimTree()
return node.copyNimTree()
proc eqIdent* (a, b: string): bool = cmpIgnoreStyle(a, b) == 0
## Check if two idents are identical
proc hasArgOfName* (params: PNimrodNode; name: string): bool {.compiletime.}=
## Search nnkFormalParams for an argument
assert params.kind == nnkFormalParams
for i in 1 .. <params.len:
template node: expr = params[i]
if name.eqIdent( $ node[0]):
return true
proc addIdentIfAbsent* (dest: PNimrodNode, ident: string) {.compiletime.} =
## Add ident to dest if it is not present. This is intended for use with pragmas
for node in dest.children:
case node.kind
of nnkIdent:
if ident.eqIdent($node): return
of nnkExprColonExpr:
if ident.eqIdent($ node[0]): return
else: nil
dest.add(ident(ident))