mirror of
https://github.com/nim-lang/Nim.git
synced 2026-02-17 16:38:33 +00:00
implements first version of for-loop macros
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
44
tests/macros/tforloop_macro1.nim
Normal file
44
tests/macros/tforloop_macro1.nim
Normal 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
|
||||
Reference in New Issue
Block a user