Add enumerate extension
This commit is contained in:
parent
eb185fa34f
commit
a69ba3c817
5 changed files with 182 additions and 3 deletions
20
README.md
20
README.md
|
|
@ -9,7 +9,7 @@ Extensions so far include:
|
||||||
|
|
||||||
This module was modified from the OOP section on the excellent website http://nim-by-example.github.io/
|
This module was modified from the OOP section on the excellent website http://nim-by-example.github.io/
|
||||||
|
|
||||||
usage:
|
Usage:
|
||||||
|
|
||||||
```nim
|
```nim
|
||||||
import extensions/oop
|
import extensions/oop
|
||||||
|
|
@ -35,3 +35,21 @@ Extensions so far include:
|
||||||
```bash
|
```bash
|
||||||
nim c -r tests/test1.nim
|
nim c -r tests/test1.nim
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Enumerate
|
||||||
|
|
||||||
|
This macro is useful for enumerating on any iterator. It will autodetect which iterator you want to use and give you a useful enumeration.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
```nim
|
||||||
|
import os
|
||||||
|
import extensions/enumerate
|
||||||
|
|
||||||
|
for i, line in enumerate(lines, "file.txt"):
|
||||||
|
echo i, " ", line
|
||||||
|
|
||||||
|
for i, tup in enumerate(walkDir, "."):
|
||||||
|
echo i, " ", tup
|
||||||
|
```
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
# Package
|
# Package
|
||||||
|
|
||||||
version = "0.2.0"
|
version = "0.2.1"
|
||||||
author = "Joey Yakimowich-Payne"
|
author = "Joey Yakimowich-Payne"
|
||||||
description = "Nim extensions"
|
description = "Nim extensions"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
|
@ -8,4 +8,4 @@ srcDir = "src"
|
||||||
|
|
||||||
# Dependencies
|
# Dependencies
|
||||||
|
|
||||||
requires "nim >= 0.18.0"
|
requires "nim >= 0.18.1"
|
||||||
|
|
|
||||||
75
src/extensions/enumerate.nim
Normal file
75
src/extensions/enumerate.nim
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
import macros
|
||||||
|
import extensions/nimnode
|
||||||
|
|
||||||
|
proc argsMatch(impl, args: NimNode): bool =
|
||||||
|
var implArgs = impl["FormalParams"]
|
||||||
|
result = true
|
||||||
|
for i, arg in args.pairs:
|
||||||
|
let implIndex = i + 1 # skip the return type
|
||||||
|
let symType = arg.getType()
|
||||||
|
let implType = implArgs[implIndex][1]
|
||||||
|
result = result and ($symType == $implType)
|
||||||
|
|
||||||
|
macro enumerate*(iter: typed, args: varargs[typed]): untyped =
|
||||||
|
## Takes in an i
|
||||||
|
var iterDef: NimNode
|
||||||
|
|
||||||
|
if len(iter) > 1:
|
||||||
|
# If there are multiple syms to choose from,
|
||||||
|
# choose the one with matching args
|
||||||
|
for c in iter:
|
||||||
|
let impl = c.getImpl().copy()
|
||||||
|
if argsMatch(impl, args):
|
||||||
|
iterDef = impl
|
||||||
|
else:
|
||||||
|
iterDef = iter.getImpl().copy()
|
||||||
|
|
||||||
|
# set the symbol to empty because
|
||||||
|
# we don't need it
|
||||||
|
iterDef["Sym"] = newEmptyNode()
|
||||||
|
|
||||||
|
# remove all stmts, we only want the def
|
||||||
|
# for now
|
||||||
|
iterDef["StmtList"] = newEmptyNode()
|
||||||
|
|
||||||
|
# remove pragma
|
||||||
|
iterDef["Pragma"] = newEmptyNode()
|
||||||
|
|
||||||
|
var
|
||||||
|
returnType = iterDef["FormalParams > 0"]
|
||||||
|
iterCall = newNimNode(nnkCall).add(iter.copy())
|
||||||
|
|
||||||
|
for child in iterDef[3].children:
|
||||||
|
if child.kind == nnkIdentDefs:
|
||||||
|
iterCall.add(child[0].copy())
|
||||||
|
|
||||||
|
# mod the return type to return both int and original type
|
||||||
|
iterDef["FormalParams > 0"] = newNimNode(nnkPar).add(ident"int", returnType)
|
||||||
|
#iterDef[3][0] = newNimNode(nnkPar).add(ident"int", returnType)
|
||||||
|
|
||||||
|
template iterBody(iterCall) =
|
||||||
|
var i = 0
|
||||||
|
for tup in iterCall:
|
||||||
|
# Since Nim's checker won't allow a yield statement here,
|
||||||
|
# we need to get creative
|
||||||
|
replace(i, tup)
|
||||||
|
inc i
|
||||||
|
|
||||||
|
var stmtList = getAst(iterBody(iterCall))
|
||||||
|
|
||||||
|
var replaceCall = stmtList["ForStmt > StmtList > Call"]
|
||||||
|
|
||||||
|
var yieldStmt = nnkYieldStmt.newTree(
|
||||||
|
nnkPar.newTree(
|
||||||
|
replaceCall[1],
|
||||||
|
replaceCall[2]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
stmtList["ForStmt > StmtList > Call"] = yieldStmt
|
||||||
|
iterDef[^1] = stmtList
|
||||||
|
|
||||||
|
# Call the iterator surrounded by parens
|
||||||
|
result = nnkCall.newTree(nnkPar.newTree(iterDef))
|
||||||
|
for arg in args:
|
||||||
|
result.add(arg)
|
||||||
1
src/extensions/nim.cfg
Normal file
1
src/extensions/nim.cfg
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
--path="../"
|
||||||
85
src/extensions/nimnode.nim
Normal file
85
src/extensions/nimnode.nim
Normal file
|
|
@ -0,0 +1,85 @@
|
||||||
|
import macros, strutils
|
||||||
|
|
||||||
|
proc `[]=`*(node: NimNode, nodePath: string, value: NimNode) =
|
||||||
|
## Set a NimNode based on a string search path like "StmtList > Command > Sym"
|
||||||
|
let nodeNames = nodePath.replace(" ", "").split(">")
|
||||||
|
|
||||||
|
let targetIndex = len(nodeNames) - 1
|
||||||
|
|
||||||
|
var
|
||||||
|
index = -1
|
||||||
|
targetParent: NimNode
|
||||||
|
stack: seq[seq[NimNode]] = @[@[node]]
|
||||||
|
indexList: seq[int] = newSeq[int](len(nodeNames))
|
||||||
|
|
||||||
|
|
||||||
|
while stack.len() > 0 and index < targetIndex:
|
||||||
|
let parentList = stack.pop()
|
||||||
|
var newParents: seq[NimNode] = @[]
|
||||||
|
|
||||||
|
block WhileBlock:
|
||||||
|
for parent in parentList:
|
||||||
|
inc index
|
||||||
|
for chIndex, child in parent.pairs:
|
||||||
|
let kindStr = ($child.kind)[3..^1]
|
||||||
|
|
||||||
|
var tryParse = -1
|
||||||
|
try:
|
||||||
|
tryParse = nodeNames[index].parseInt
|
||||||
|
except ValueError:
|
||||||
|
discard
|
||||||
|
|
||||||
|
if kindStr == nodeNames[index] or tryParse == chIndex:
|
||||||
|
targetParent = parent
|
||||||
|
newParents.add(child)
|
||||||
|
indexList[index] = chIndex
|
||||||
|
|
||||||
|
if index == targetIndex:
|
||||||
|
break WhileBlock
|
||||||
|
|
||||||
|
if newParents.len() > 0:
|
||||||
|
stack.add(newParents)
|
||||||
|
|
||||||
|
if index == targetIndex:
|
||||||
|
let modIndex = indexList.pop()
|
||||||
|
var curNode = node
|
||||||
|
for i in indexList:
|
||||||
|
curNode = curNode[i]
|
||||||
|
|
||||||
|
curNode[modIndex] = value
|
||||||
|
|
||||||
|
|
||||||
|
proc `[]`*(node: NimNode, nodePath: string): NimNode =
|
||||||
|
## Get a NimNode based on a string search path like "StmtList > Command > Sym"
|
||||||
|
let nodeNames = nodePath.replace(" ", "").split(">")
|
||||||
|
|
||||||
|
let targetIndex = len(nodeNames) - 1
|
||||||
|
|
||||||
|
var
|
||||||
|
index = -1
|
||||||
|
stack: seq[seq[NimNode]] = @[@[node]]
|
||||||
|
|
||||||
|
while stack.len() > 0 and index < targetIndex:
|
||||||
|
let parentList = stack.pop()
|
||||||
|
var newParents: seq[NimNode] = @[]
|
||||||
|
|
||||||
|
block WhileBlock:
|
||||||
|
for parent in parentList:
|
||||||
|
inc index
|
||||||
|
for i, child in parent.pairs:
|
||||||
|
let kindStr = ($child.kind)[3..^1]
|
||||||
|
|
||||||
|
var tryParse = -1
|
||||||
|
try:
|
||||||
|
tryParse = nodeNames[index].parseInt
|
||||||
|
except ValueError:
|
||||||
|
discard
|
||||||
|
|
||||||
|
if kindStr == nodeNames[index] or tryParse == i:
|
||||||
|
result = child
|
||||||
|
newParents.add(child)
|
||||||
|
if index == targetIndex:
|
||||||
|
break WhileBlock
|
||||||
|
|
||||||
|
if newParents.len() > 0:
|
||||||
|
stack.add(newParents)
|
||||||
Loading…
Add table
Add a link
Reference in a new issue