implements first version of for-loop macros
This commit is contained in:
parent
a30b52eb64
commit
c08efb4c51
4 changed files with 89 additions and 2 deletions
|
|
@ -50,6 +50,9 @@
|
||||||
- Dot calls combined with explicit generic instantiations can now be written
|
- 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.
|
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.}``.
|
- ``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
|
### Language changes
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -702,11 +702,46 @@ proc isTrivalStmtExpr(n: PNode): bool =
|
||||||
return false
|
return false
|
||||||
result = true
|
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 =
|
proc semFor(c: PContext, n: PNode): PNode =
|
||||||
result = n
|
|
||||||
checkMinSonsLen(n, 3)
|
checkMinSonsLen(n, 3)
|
||||||
var length = sonsLen(n)
|
var length = sonsLen(n)
|
||||||
|
result = handleForLoopMacro(c, n)
|
||||||
|
if result != nil: return result
|
||||||
openScope(c)
|
openScope(c)
|
||||||
|
result = n
|
||||||
n.sons[length-2] = semExprNoDeref(c, n.sons[length-2], {efWantIterator})
|
n.sons[length-2] = semExprNoDeref(c, n.sons[length-2], {efWantIterator})
|
||||||
var call = n.sons[length-2]
|
var call = n.sons[length-2]
|
||||||
if call.kind == nkStmtListExpr and isTrivalStmtExpr(call):
|
if call.kind == nkStmtListExpr and isTrivalStmtExpr(call):
|
||||||
|
|
|
||||||
|
|
@ -4172,3 +4172,8 @@ when not defined(js):
|
||||||
magic: "Slice".}
|
magic: "Slice".}
|
||||||
proc toOpenArray*(x: string; first, last: int): openarray[char] {.
|
proc toOpenArray*(x: string; first, last: int): openarray[char] {.
|
||||||
magic: "Slice".}
|
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
|
||||||
Loading…
Add table
Add a link
Reference in a new issue