implements first version of for-loop macros

This commit is contained in:
Andreas Rumpf
2018-04-15 01:07:28 +02:00
parent a30b52eb64
commit c08efb4c51
4 changed files with 89 additions and 2 deletions

View File

@@ -50,6 +50,9 @@
- Dot calls combined with explicit generic instantiations can now be written
as ``x.y[:z]``. ``x.y[:z]`` that is transformed into ``y[z](x)`` in the parser.
- ``func`` is now an alias for ``proc {.noSideEffect.}``.
- In order to make ``for`` loops and iterators more flexible to use Nim now
supports so called "for-loop macros". See
the `manual <manual.html#macros-for-loop-macros>`_ for more details.
### Language changes

View File

@@ -702,11 +702,46 @@ proc isTrivalStmtExpr(n: PNode): bool =
return false
result = true
proc handleForLoopMacro(c: PContext; n: PNode): PNode =
let iterExpr = n[^2]
if iterExpr.kind in nkCallKinds:
# we transform
# n := for a, b, c in m(x, y, z): Y
# to
# m(n)
let forLoopStmt = magicsys.getCompilerProc("ForLoopStmt")
if forLoopStmt == nil: return
let headSymbol = iterExpr[0]
var o: TOverloadIter
var match: PSym = nil
var symx = initOverloadIter(o, c, headSymbol)
while symx != nil:
if symx.kind in {skTemplate, skMacro}:
if symx.typ.len == 2 and symx.typ[1] == forLoopStmt.typ:
if match == nil:
match = symx
else:
localError(n.info, errGenerated, msgKindToString(errAmbiguousCallXYZ) % [
getProcHeader(match), getProcHeader(symx), $iterExpr])
symx = nextOverloadIter(o, c, headSymbol)
if match == nil: return
var callExpr = newNodeI(nkCall, n.info)
callExpr.add newSymNode(match)
callExpr.add n
case match.kind
of skMacro: result = semMacroExpr(c, callExpr, callExpr, match, {})
of skTemplate: result = semTemplateExpr(c, callExpr, match, {})
else: result = nil
proc semFor(c: PContext, n: PNode): PNode =
result = n
checkMinSonsLen(n, 3)
var length = sonsLen(n)
result = handleForLoopMacro(c, n)
if result != nil: return result
openScope(c)
result = n
n.sons[length-2] = semExprNoDeref(c, n.sons[length-2], {efWantIterator})
var call = n.sons[length-2]
if call.kind == nkStmtListExpr and isTrivalStmtExpr(call):
@@ -772,7 +807,7 @@ proc typeSectionTypeName(n: PNode): PNode =
else:
result = n
if result.kind != nkSym: illFormedAst(n)
proc typeSectionLeftSidePass(c: PContext, n: PNode) =
# process the symbols on the left side for the whole type section, before

View File

@@ -4172,3 +4172,8 @@ when not defined(js):
magic: "Slice".}
proc toOpenArray*(x: string; first, last: int): openarray[char] {.
magic: "Slice".}
type
ForLoopStmt* {.compilerProc.} = object ## special type that marks a macro
## as a `for-loop macro`:idx:

View File

@@ -0,0 +1,44 @@
discard """
output: '''0 1
1 2
2 3
0 1
1 2
2 3
0 1
1 2
2 3
3 5'''
"""
import macros
macro mymacro(): untyped =
result = newLit([1, 2, 3])
for a, b in mymacro():
echo a, " ", b
macro enumerate(x: ForLoopStmt): untyped =
expectKind x, nnkForStmt
# we strip off the first for loop variable and use
# it as an integer counter:
result = newStmtList()
result.add newVarStmt(x[0], newLit(0))
var body = x[^1]
if body.kind != nnkStmtList:
body = newTree(nnkStmtList, body)
body.add newCall(bindSym"inc", x[0])
var newFor = newTree(nnkForStmt)
for i in 1..x.len-3:
newFor.add x[i]
# transform enumerate(X) to 'X'
newFor.add x[^2][1]
newFor.add body
result.add newFor
for a, b in enumerate(items([1, 2, 3])):
echo a, " ", b
for a2, b2 in enumerate([1, 2, 3, 5]):
echo a2, " ", b2