summary refs log tree commit diff stats
path: root/lib/core/macros.nim
diff options
context:
space:
mode:
Diffstat (limited to 'lib/core/macros.nim')
-rw-r--r--lib/core/macros.nim233
1 files changed, 233 insertions, 0 deletions
diff --git a/lib/core/macros.nim b/lib/core/macros.nim
index 949719316..a9479063e 100644
--- a/lib/core/macros.nim
+++ b/lib/core/macros.nim
@@ -410,3 +410,236 @@ macro dumpTreeImm*(s: stmt): stmt {.immediate.} = echo s.treeRepr
 macro dumpLispImm*(s: stmt): stmt {.immediate.} = echo s.lispRepr
   ## The ``immediate`` version of `dumpLisp`.
 
+
+import strutils
+
+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()]` 
+
+
+template ProcLikeNodes*:Expr = {nnkProcDef, nnkMethodDef, nnkDo, nnkLambda}
+const NoSonsNodes = {nnkNone, nnkEmpty, nnkNilLit,
+  nnkCharLit .. nnkInt64Lit, nnkFLoatLit .. nnkFloat64Lit, 
+  nnkStrLit .. nnkTripleStrLit, nnkIdent, nnkSym }
+
+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 ProcLikeNodes
+  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
+
+proc name*(someProc: PNimrodNode): PNimrodNode {.compileTime.} =
+  someProc.expectKind ProcLikeNodes
+  result = someProc[0]
+proc `name=`*(someProc: PNimrodNode; val: PNimrodNode) {.compileTime.} =
+  someProc.expectKind ProcLikeNodes
+  someProc[0] = val
+
+proc params*(someProc: PNimrodNode): PNimrodNode {.compileTime.} =
+  someProc.expectKind procLikeNodes
+  result = someProc[3]
+proc `params=`* (someProc: PNimrodNode; params: PNimrodNode) {.compileTime.}=
+  someProc.expectKind procLikeNodes
+  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.expectKind procLikeNodes
+  result = someProc[4]
+proc `pragma=`*(someProc: PNimrodNode; val: PNimrodNode){.compileTime.}=
+  ## Set the pragma of a proc type
+  someProc.expectKind procLikeNodes
+  assert val.kind in {nnkEmpty, nnkPragma}
+  someProc[4] = val
+
+
+template badnodekind(k; f): stmt{.immediate.} =
+  assert false, "Invalid node kind $# for macros.`$2`" % [$k, f]
+
+proc body*(someProc: PNimrodNode): PNimrodNode {.compileTime.} =
+  case someProc.kind:
+  of procLikeNodes:
+    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 ProcLikeNodes:
+    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 `!`*(a: TNimrodIdent): PNimrodNode {.compileTime, inline.} = newIdentNode(a)
+  ## Create a new ident node from an identifier
+proc `!!`*(a: string): PNimrodNode {.compileTime, inline.} = newIdentNode(a)
+  ## Create a new ident node from a string
+  ## The same as !(!(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: TNimrodIdent) {.compileTime.}=
+  case a.kind
+  of nnkIdent: a.ident = val
+  of nnkPostfix, nnkPrefix: a[1] = !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(!!op, node)
+proc prefix*(node: PNimrodNode; op: string): PNimrodNode {.
+  compileTime.} = newNimNode(nnkPrefix).add(!!op, node)
+proc infix*(a: PNimrodNode; op: string; b: PNimrodNode): PNimrodNode {.
+  compileTime.} = newNimNode(nnkInfix).add(!!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)
+
id='n489' href='#n489'>489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610