Merge pull request #8949 from nim-lang/araq-for-loop-expressions

For loop expressions
This commit is contained in:
Andreas Rumpf
2018-09-12 10:27:54 +02:00
committed by GitHub
9 changed files with 163 additions and 81 deletions

View File

@@ -16,7 +16,7 @@ import
wordrecg, syntaxes, renderer, lexer, packages/docutils/rstast,
packages/docutils/rst, packages/docutils/rstgen,
packages/docutils/highlite, sempass2, json, xmltree, cgi,
typesrenderer, astalgo, modulepaths, lineinfos, sequtils
typesrenderer, astalgo, modulepaths, lineinfos, sequtils, intsets
type
TSections = array[TSymKind, Rope]
@@ -32,6 +32,8 @@ type
conf*: ConfigRef
cache*: IdentCache
exampleCounter: int
emitted: IntSet # we need to track which symbols have been emitted
# already. See bug #3655
PDoc* = ref TDocumentor ## Alias to type less.
@@ -119,6 +121,7 @@ proc newDocumentor*(filename: string; cache: IdentCache; conf: ConfigRef): PDoc
initStrTable result.types
result.onTestSnippet = proc (d: var RstGenerator; filename, cmd: string; status: int; content: string) =
localError(conf, newLineInfo(conf, d.filename, -1, -1), warnUser, "only 'rst2html' supports the ':test:' attribute")
result.emitted = initIntSet()
proc dispA(conf: ConfigRef; dest: var Rope, xml, tex: string, args: openArray[Rope]) =
if conf.cmd != cmdRst2tex: addf(dest, xml, args)
@@ -377,7 +380,7 @@ when false:
else:
result = n.comment.substr(2).replace("\n##", "\n").strip
proc isVisible(n: PNode): bool =
proc isVisible(d: PDoc; n: PNode): bool =
result = false
if n.kind == nkPostfix:
if n.len == 2 and n.sons[0].kind == nkIdent:
@@ -388,8 +391,10 @@ proc isVisible(n: PNode): bool =
# exception tracking information here. Instead we copy over the comment
# from the proc header.
result = {sfExported, sfFromGeneric, sfForward}*n.sym.flags == {sfExported}
if result and containsOrIncl(d.emitted, n.sym.id):
result = false
elif n.kind == nkPragmaExpr:
result = isVisible(n.sons[0])
result = isVisible(d, n.sons[0])
proc getName(d: PDoc, n: PNode, splitAfter = -1): string =
case n.kind
@@ -520,7 +525,7 @@ proc docstringSummary(rstText: string): string =
proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) =
if not isVisible(nameNode): return
if not isVisible(d, nameNode): return
let
name = getName(d, nameNode)
nameRope = name.rope
@@ -601,7 +606,7 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) =
d.types.strTableAdd nameNode.sym
proc genJsonItem(d: PDoc, n, nameNode: PNode, k: TSymKind): JsonNode =
if not isVisible(nameNode): return
if not isVisible(d, nameNode): return
var
name = getName(d, nameNode)
comm = $genRecComment(d, n)

View File

@@ -537,7 +537,7 @@ proc parsePar(p: var TParser): PNode =
flexComment(p, result)
if p.tok.tokType in {tkDiscard, tkInclude, tkIf, tkWhile, tkCase,
tkTry, tkDefer, tkFinally, tkExcept, tkFor, tkBlock,
tkConst, tkLet, tkWhen, tkVar,
tkConst, tkLet, tkWhen, tkVar, tkFor,
tkMixin}:
# XXX 'bind' used to be an expression, so we exclude it here;
# tests/reject/tbind2 fails otherwise.
@@ -1112,7 +1112,7 @@ proc parseProcExpr(p: var TParser; isExpr: bool; kind: TNodeKind): PNode =
proc isExprStart(p: TParser): bool =
case p.tok.tokType
of tkSymbol, tkAccent, tkOpr, tkNot, tkNil, tkCast, tkIf,
of tkSymbol, tkAccent, tkOpr, tkNot, tkNil, tkCast, tkIf, tkFor,
tkProc, tkFunc, tkIterator, tkBind, tkBuiltInMagics,
tkParLe, tkBracketLe, tkCurlyLe, tkIntLit..tkCharLit, tkVar, tkRef, tkPtr,
tkTuple, tkObject, tkWhen, tkCase, tkOut:
@@ -1152,16 +1152,35 @@ proc parseTypeDescKAux(p: var TParser, kind: TNodeKind,
result.addSon list
parseSymbolList(p, list)
proc parseFor(p: var TParser): PNode =
#| forStmt = 'for' (identWithPragma ^+ comma) 'in' expr colcom stmt
#| forExpr = forStmt
result = newNodeP(nkForStmt, p)
getTokNoInd(p)
var a = identWithPragma(p)
addSon(result, a)
while p.tok.tokType == tkComma:
getTok(p)
optInd(p, a)
a = identWithPragma(p)
addSon(result, a)
eat(p, tkIn)
addSon(result, parseExpr(p))
colcom(p, result)
addSon(result, parseStmt(p))
proc parseExpr(p: var TParser): PNode =
#| expr = (blockExpr
#| | ifExpr
#| | whenExpr
#| | caseExpr
#| | forExpr
#| | tryExpr)
#| / simpleExpr
case p.tok.tokType:
of tkBlock: result = parseBlock(p)
of tkIf: result = parseIfExpr(p, nkIfExpr)
of tkFor: result = parseFor(p)
of tkWhen: result = parseIfExpr(p, nkWhenExpr)
of tkCase: result = parseCase(p)
of tkTry: result = parseTry(p, isExpr=true)
@@ -1568,22 +1587,6 @@ proc parseExceptBlock(p: var TParser, kind: TNodeKind): PNode =
colcom(p, result)
addSon(result, parseStmt(p))
proc parseFor(p: var TParser): PNode =
#| forStmt = 'for' (identWithPragma ^+ comma) 'in' expr colcom stmt
result = newNodeP(nkForStmt, p)
getTokNoInd(p)
var a = identWithPragma(p)
addSon(result, a)
while p.tok.tokType == tkComma:
getTok(p)
optInd(p, a)
a = identWithPragma(p)
addSon(result, a)
eat(p, tkIn)
addSon(result, parseExpr(p))
colcom(p, result)
addSon(result, parseStmt(p))
proc parseBlock(p: var TParser): PNode =
#| blockStmt = 'block' symbol? colcom stmt
#| blockExpr = 'block' symbol? colcom stmt

View File

@@ -37,7 +37,7 @@ proc changeType(c: PContext; n: PNode, newType: PType, check: bool)
proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode
proc semTypeNode(c: PContext, n: PNode, prev: PType): PType
proc semStmt(c: PContext, n: PNode): PNode
proc semStmt(c: PContext, n: PNode; flags: TExprFlags): PNode
proc semOpAux(c: PContext, n: PNode)
proc semParamList(c: PContext, n, genericParams: PNode, s: PSym)
proc addParams(c: PContext, n: PNode, kind: TSymKind)
@@ -399,7 +399,7 @@ proc semAfterMacroCall(c: PContext, call, macroResult: PNode,
excl(result.flags, nfSem)
#resetSemFlag n
if s.typ.sons[0] == nil:
result = semStmt(c, result)
result = semStmt(c, result, flags)
else:
case s.typ.sons[0].kind
of tyExpr:
@@ -408,7 +408,7 @@ proc semAfterMacroCall(c: PContext, call, macroResult: PNode,
# semExprWithType(c, result)
result = semExpr(c, result, flags)
of tyStmt:
result = semStmt(c, result)
result = semStmt(c, result, flags)
of tyTypeDesc:
if result.kind == nkStmtList: result.kind = nkStmtListType
var typ = semTypeNode(c, result, nil)
@@ -557,7 +557,7 @@ proc semStmtAndGenerateGenerics(c: PContext, n: PNode): PNode =
result = semAllTypeSections(c, n)
else:
result = n
result = semStmt(c, result)
result = semStmt(c, result, {})
when false:
# Code generators are lazy now and can deal with undeclared procs, so these
# steps are not required anymore and actually harmful for the upcoming

View File

@@ -918,7 +918,7 @@ proc semExprNoType(c: PContext, n: PNode): PNode =
let isPush = hintExtendedContext in c.config.notes
if isPush: pushInfoContext(c.config, n.info)
result = semExpr(c, n, {efWantStmt})
discardCheck(c, result)
discardCheck(c, result, {})
if isPush: popInfoContext(c.config)
proc isTypeExpr(n: PNode): bool =
@@ -1530,7 +1530,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
# unfortunately we need to rewrite ``(x, y) = foo()`` already here so
# that overloading of the assignment operator still works. Usually we
# prefer to do these rewritings in transf.nim:
return semStmt(c, lowerTupleUnpackingForAsgn(c.graph, n, c.p.owner))
return semStmt(c, lowerTupleUnpackingForAsgn(c.graph, n, c.p.owner), {})
else:
a = semExprWithType(c, a, {efLValue})
else:
@@ -1614,7 +1614,7 @@ proc semProcBody(c: PContext, n: PNode): PNode =
a.sons[1] = result
result = semAsgn(c, a)
else:
discardCheck(c, result)
discardCheck(c, result, {})
if c.p.owner.kind notin {skMacro, skTemplate} and
c.p.resultSym != nil and c.p.resultSym.typ.isMetaType:
@@ -1990,7 +1990,7 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
var x = n.lastSon
if x.kind == nkDo: x = x.sons[bodyPos]
inc c.inParallelStmt
result.sons[1] = semStmt(c, x)
result.sons[1] = semStmt(c, x, {})
dec c.inParallelStmt
of mSpawn:
result = setMs(n, s)
@@ -2241,7 +2241,7 @@ proc isTupleType(n: PNode): bool =
include semobjconstr
proc semBlock(c: PContext, n: PNode): PNode =
proc semBlock(c: PContext, n: PNode; flags: TExprFlags): PNode =
result = n
inc(c.p.nestedBlockCounter)
checkSonsLen(n, 2, c.config)
@@ -2253,7 +2253,7 @@ proc semBlock(c: PContext, n: PNode): PNode =
n.sons[0] = newSymNode(labl, n.sons[0].info)
suggestSym(c.config, n.sons[0].info, labl, c.graph.usageSym)
styleCheckDef(c.config, labl)
n.sons[1] = semExpr(c, n.sons[1])
n.sons[1] = semExpr(c, n.sons[1], flags)
n.typ = n.sons[1].typ
if isEmptyType(n.typ): n.kind = nkBlockStmt
else: n.kind = nkBlockExpr
@@ -2498,7 +2498,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
checkSonsLen(n, 1, c.config)
n.sons[0] = semExpr(c, n.sons[0], flags)
of nkCast: result = semCast(c, n)
of nkIfExpr, nkIfStmt: result = semIf(c, n)
of nkIfExpr, nkIfStmt: result = semIf(c, n, flags)
of nkHiddenStdConv, nkHiddenSubConv, nkConv, nkHiddenCallConv:
checkSonsLen(n, 2, c.config)
considerGenSyms(c, n)
@@ -2519,7 +2519,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
discard
of nkStaticExpr: result = semStaticExpr(c, n[0])
of nkAsgn: result = semAsgn(c, n)
of nkBlockStmt, nkBlockExpr: result = semBlock(c, n)
of nkBlockStmt, nkBlockExpr: result = semBlock(c, n, flags)
of nkStmtList, nkStmtListExpr: result = semStmtList(c, n, flags)
of nkRaiseStmt: result = semRaise(c, n)
of nkVarSection: result = semVarOrLet(c, n, skVar)
@@ -2527,11 +2527,11 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
of nkConstSection: result = semConst(c, n)
of nkTypeSection: result = semTypeSection(c, n)
of nkDiscardStmt: result = semDiscard(c, n)
of nkWhileStmt: result = semWhile(c, n)
of nkTryStmt: result = semTry(c, n)
of nkWhileStmt: result = semWhile(c, n, flags)
of nkTryStmt: result = semTry(c, n, flags)
of nkBreakStmt, nkContinueStmt: result = semBreakOrContinue(c, n)
of nkForStmt, nkParForStmt: result = semFor(c, n)
of nkCaseStmt: result = semCase(c, n)
of nkForStmt, nkParForStmt: result = semFor(c, n, flags)
of nkCaseStmt: result = semCase(c, n, flags)
of nkReturnStmt: result = semReturn(c, n)
of nkUsingStmt: result = semUsing(c, n)
of nkAsmStmt: result = semAsm(c, n)

View File

@@ -70,7 +70,7 @@ proc semForObjectFields(c: TFieldsCtx, typ, forLoop, father: PNode) =
openScope(c.c)
inc c.c.inUnrolledContext
let body = instFieldLoopBody(fc, lastSon(forLoop), forLoop)
father.add(semStmt(c.c, body))
father.add(semStmt(c.c, body, {}))
dec c.c.inUnrolledContext
closeScope(c.c)
of nkNilLit: discard
@@ -145,7 +145,7 @@ proc semForFields(c: PContext, n: PNode, m: TMagic): PNode =
fc.replaceByFieldName = m == mFieldPairs
var body = instFieldLoopBody(fc, loopBody, n)
inc c.inUnrolledContext
stmts.add(semStmt(c, body))
stmts.add(semStmt(c, body, {}))
dec c.inUnrolledContext
closeScope(c)
else:

View File

@@ -76,17 +76,19 @@ proc semAsm(c: PContext, n: PNode): PNode =
if marker == '\0': marker = '`' # default marker
result = semAsmOrEmit(c, n, marker)
proc semWhile(c: PContext, n: PNode): PNode =
proc semWhile(c: PContext, n: PNode; flags: TExprFlags): PNode =
result = n
checkSonsLen(n, 2, c.config)
openScope(c)
n.sons[0] = forceBool(c, semExprWithType(c, n.sons[0]))
inc(c.p.nestedLoopCounter)
n.sons[1] = semStmt(c, n.sons[1])
n.sons[1] = semStmt(c, n.sons[1], flags)
dec(c.p.nestedLoopCounter)
closeScope(c)
if n.sons[1].typ == c.enforceVoidContext:
result.typ = c.enforceVoidContext
elif efInTypeof in flags:
result.typ = n[1].typ
proc toCover(c: PContext, t: PType): BiggestInt =
let t2 = skipTypes(t, abstractVarRange-{tyTypeDesc})
@@ -97,8 +99,8 @@ proc toCover(c: PContext, t: PType): BiggestInt =
proc semProc(c: PContext, n: PNode): PNode
proc semExprBranch(c: PContext, n: PNode): PNode =
result = semExpr(c, n)
proc semExprBranch(c: PContext, n: PNode; flags: TExprFlags = {}): PNode =
result = semExpr(c, n, flags)
if result.typ != nil:
# XXX tyGenericInst here?
if result.typ.kind in {tyVar, tyLent}: result = newDeref(result)
@@ -130,8 +132,9 @@ proc fixNilType(c: PContext; n: PNode) =
for it in n: fixNilType(c, it)
n.typ = nil
proc discardCheck(c: PContext, result: PNode) =
if c.matchedConcept != nil: return
proc discardCheck(c: PContext, result: PNode, flags: TExprFlags) =
if c.matchedConcept != nil or efInTypeof in flags: return
if result.typ != nil and result.typ.kind notin {tyStmt, tyVoid}:
if implicitlyDiscardable(result):
var n = newNodeI(nkDiscardStmt, result.info, 1)
@@ -148,7 +151,7 @@ proc discardCheck(c: PContext, result: PNode) =
s.add "; for a function call use ()"
localError(c.config, n.info, s)
proc semIf(c: PContext, n: PNode): PNode =
proc semIf(c: PContext, n: PNode; flags: TExprFlags): PNode =
result = n
var typ = commonTypeBegin
var hasElse = false
@@ -165,8 +168,9 @@ proc semIf(c: PContext, n: PNode): PNode =
it.sons[0] = semExprBranchScope(c, it.sons[0])
typ = commonType(typ, it.sons[0])
else: illFormedAst(it, c.config)
if isEmptyType(typ) or typ.kind in {tyNil, tyExpr} or not hasElse:
for it in n: discardCheck(c, it.lastSon)
if isEmptyType(typ) or typ.kind in {tyNil, tyExpr} or
(not hasElse and efInTypeof notin flags):
for it in n: discardCheck(c, it.lastSon, flags)
result.kind = nkIfStmt
# propagate any enforced VoidContext:
if typ == c.enforceVoidContext: result.typ = c.enforceVoidContext
@@ -178,8 +182,7 @@ proc semIf(c: PContext, n: PNode): PNode =
result.kind = nkIfExpr
result.typ = typ
proc semTry(c: PContext, n: PNode): PNode =
proc semTry(c: PContext, n: PNode; flags: TExprFlags): PNode =
var check = initIntSet()
template semExceptBranchType(typeNode: PNode): bool =
# returns true if exception type is imported type
@@ -246,12 +249,12 @@ proc semTry(c: PContext, n: PNode): PNode =
dec c.p.inTryStmt
if isEmptyType(typ) or typ.kind in {tyNil, tyExpr}:
discardCheck(c, n.sons[0])
for i in 1..n.len-1: discardCheck(c, n.sons[i].lastSon)
discardCheck(c, n.sons[0], flags)
for i in 1..n.len-1: discardCheck(c, n.sons[i].lastSon, flags)
if typ == c.enforceVoidContext:
result.typ = c.enforceVoidContext
else:
if n.lastSon.kind == nkFinally: discardCheck(c, n.lastSon.lastSon)
if n.lastSon.kind == nkFinally: discardCheck(c, n.lastSon.lastSon, flags)
n.sons[0] = fitNode(c, typ, n.sons[0], n.sons[0].info)
for i in 1..last:
var it = n.sons[i]
@@ -570,7 +573,7 @@ proc symForVar(c: PContext, n: PNode): PSym =
if n.kind == nkPragmaExpr:
pragma(c, result, n.sons[1], forVarPragmas)
proc semForVars(c: PContext, n: PNode): PNode =
proc semForVars(c: PContext, n: PNode; flags: TExprFlags): PNode =
result = n
var length = sonsLen(n)
let iterBase = n.sons[length-2].typ
@@ -601,7 +604,7 @@ proc semForVars(c: PContext, n: PNode): PNode =
addForVarDecl(c, v)
inc(c.p.nestedLoopCounter)
openScope(c)
n.sons[length-1] = semStmt(c, n.sons[length-1])
n.sons[length-1] = semExprBranch(c, n.sons[length-1], flags)
closeScope(c)
dec(c.p.nestedLoopCounter)
@@ -685,7 +688,7 @@ proc handleCaseStmtMacro(c: PContext; n: PNode): PNode =
when false:
result = handleStmtMacro(c, n, n[0], "CaseStmt")
proc semFor(c: PContext, n: PNode): PNode =
proc semFor(c: PContext, n: PNode; flags: TExprFlags): PNode =
checkMinSonsLen(n, 3, c.config)
var length = sonsLen(n)
if forLoopMacros in c.features:
@@ -702,14 +705,14 @@ proc semFor(c: PContext, n: PNode): PNode =
if isCallExpr and call[0].kind == nkSym and
call[0].sym.magic in {mFields, mFieldPairs, mOmpParFor}:
if call.sons[0].sym.magic == mOmpParFor:
result = semForVars(c, n)
result = semForVars(c, n, flags)
result.kind = nkParForStmt
else:
result = semForFields(c, n, call.sons[0].sym.magic)
elif isCallExpr and call.sons[0].typ.callConv == ccClosure and
tfIterator in call.sons[0].typ.flags:
# first class iterator:
result = semForVars(c, n)
result = semForVars(c, n, flags)
elif not isCallExpr or call.sons[0].kind != nkSym or
call.sons[0].sym.kind != skIterator:
if length == 3:
@@ -718,15 +721,17 @@ proc semFor(c: PContext, n: PNode): PNode =
n.sons[length-2] = implicitIterator(c, "pairs", n.sons[length-2])
else:
localError(c.config, n.sons[length-2].info, "iterator within for loop context expected")
result = semForVars(c, n)
result = semForVars(c, n, flags)
else:
result = semForVars(c, n)
result = semForVars(c, n, flags)
# propagate any enforced VoidContext:
if n.sons[length-1].typ == c.enforceVoidContext:
result.typ = c.enforceVoidContext
elif efInTypeof in flags:
result.typ = result.lastSon.typ
closeScope(c)
proc semCase(c: PContext, n: PNode): PNode =
proc semCase(c: PContext, n: PNode; flags: TExprFlags): PNode =
result = n
checkMinSonsLen(n, 2, c.config)
openScope(c)
@@ -782,8 +787,9 @@ proc semCase(c: PContext, n: PNode): PNode =
else:
localError(c.config, n.info, "not all cases are covered")
closeScope(c)
if isEmptyType(typ) or typ.kind in {tyNil, tyExpr} or not hasElse:
for i in 1..n.len-1: discardCheck(c, n.sons[i].lastSon)
if isEmptyType(typ) or typ.kind in {tyNil, tyExpr} or
(not hasElse and efInTypeof notin flags):
for i in 1..n.len-1: discardCheck(c, n.sons[i].lastSon, flags)
# propagate any enforced VoidContext:
if typ == c.enforceVoidContext:
result.typ = c.enforceVoidContext
@@ -1795,7 +1801,7 @@ proc evalInclude(c: PContext, n: PNode): PNode =
if containsOrIncl(c.includedFiles, f.int):
localError(c.config, n.info, errRecursiveDependencyX % toFilename(c.config, f))
else:
addSon(result, semStmt(c, c.graph.includeFileCallback(c.graph, c.module, f)))
addSon(result, semStmt(c, c.graph.includeFileCallback(c.graph, c.module, f), {}))
excl(c.includedFiles, f.int)
proc setLine(n: PNode, info: TLineInfo) =
@@ -1822,7 +1828,7 @@ proc semStaticStmt(c: PContext, n: PNode): PNode =
#writeStackTrace()
inc c.inStaticContext
openScope(c)
let a = semStmt(c, n.sons[0])
let a = semStmt(c, n.sons[0], {})
closeScope(c)
dec c.inStaticContext
n.sons[0] = a
@@ -1898,11 +1904,11 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
if n.sons[i].typ == c.enforceVoidContext: #or usesResult(n.sons[i]):
voidContext = true
n.typ = c.enforceVoidContext
if i == last and (length == 1 or efWantValue in flags):
if i == last and (length == 1 or ({efWantValue, efInTypeof} * flags != {})):
n.typ = n.sons[i].typ
if not isEmptyType(n.typ): n.kind = nkStmtListExpr
elif i != last or voidContext:
discardCheck(c, n.sons[i])
discardCheck(c, n.sons[i], flags)
else:
n.typ = n.sons[i].typ
if not isEmptyType(n.typ): n.kind = nkStmtListExpr
@@ -1931,6 +1937,8 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
# it is an old-style comment statement: we replace it with 'discard ""':
prettybase.replaceComment(result.info)
proc semStmt(c: PContext, n: PNode): PNode =
# now: simply an alias:
result = semExprNoType(c, n)
proc semStmt(c: PContext, n: PNode; flags: TExprFlags): PNode =
if efInTypeof notin flags:
result = semExprNoType(c, n)
else:
result = semExpr(c, n, flags)

View File

@@ -1161,7 +1161,7 @@ proc semStmtListType(c: PContext, n: PNode, prev: PType): PType =
checkMinSonsLen(n, 1, c.config)
var length = sonsLen(n)
for i in countup(0, length - 2):
n.sons[i] = semStmt(c, n.sons[i])
n.sons[i] = semStmt(c, n.sons[i], {})
if length > 0:
result = semTypeNode(c, n.sons[length - 1], prev)
n.typ = result
@@ -1406,6 +1406,13 @@ proc semStaticType(c: PContext, childNode: PNode, prev: PType): PType =
result.rawAddSon(base)
result.flags.incl tfHasStatic
proc semTypeof(c: PContext; n: PNode; prev: PType): PType =
openScope(c)
let t = semExprWithType(c, n, {efInTypeof})
closeScope(c)
fixupTypeOf(c, prev, t)
result = t.typ
proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
result = nil
inc c.inTypeContext
@@ -1416,9 +1423,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
of nkTypeOfExpr:
# for ``type(countup(1,3))``, see ``tests/ttoseq``.
checkSonsLen(n, 1, c.config)
let typExpr = semExprWithType(c, n.sons[0], {efInTypeof})
fixupTypeOf(c, prev, typExpr)
result = typExpr.typ
result = semTypeof(c, n.sons[0], prev)
if result.kind == tyTypeDesc: result.flags.incl tfExplicit
of nkPar:
if sonsLen(n) == 1: result = semTypeNode(c, n.sons[0], prev)
@@ -1487,9 +1492,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
result = semAnyRef(c, n, tyRef, prev)
elif op.id == ord(wType):
checkSonsLen(n, 2, c.config)
let typExpr = semExprWithType(c, n.sons[1], {efInTypeof})
fixupTypeOf(c, prev, typExpr)
result = typExpr.typ
result = semTypeof(c, n[1], prev)
else:
if c.inGenericContext > 0 and n.kind == nkCall:
result = makeTypeFromExpr(c, n.copyTree)

View File

@@ -240,7 +240,7 @@ else: # bootstrapping substitute
when defined(nimHasSymOwnerInMacro):
proc owner*(sym: NimNode): NimNode {.magic: "SymOwner", noSideEffect.}
## accepts node of kind nnkSym and returns its owner's symbol.
## result is also mnde of kind nnkSym if owner exists otherwise
## result is also mnde of kind nnkSym if owner exists otherwise
## nnkNilLit is returned
proc getType*(n: NimNode): NimNode {.magic: "NGetType", noSideEffect.}
@@ -977,7 +977,7 @@ proc newIfStmt*(branches: varargs[tuple[cond, body: NimNode]]):
##
result = newNimNode(nnkIfStmt)
for i in branches:
result.add(newNimNode(nnkElifBranch).add(i.cond, i.body))
result.add(newTree(nnkElifBranch, i.cond, i.body))
proc newEnum*(name: NimNode, fields: openArray[NimNode],
public, pure: bool): NimNode {.compileTime.} =

63
tests/macros/tcollect.nim Normal file
View File

@@ -0,0 +1,63 @@
discard """
output: '''@[2, 3, 4, 2, 3, 4, 2, 3, 4, 2, 3, 4, 2, 3, 4]
@[0, 1, 2, 3]'''
"""
const data = [1,2,3,4,5,6]
import macros
macro collect(body): untyped =
# analyse the body, find the deepest expression 'it' and replace it via
# 'result.add it'
let res = genSym(nskVar, "collectResult")
when false:
proc detectForLoopVar(n: NimNode): NimNode =
if n.kind == nnkForStmt:
result = n[0]
else:
for x in n:
result = detectForLoopVar(x)
if result != nil: return result
return nil
proc t(n, res: NimNode): NimNode =
case n.kind
of nnkStmtList, nnkStmtListExpr, nnkBlockStmt, nnkBlockExpr,
nnkWhileStmt,
nnkForStmt, nnkIfExpr, nnkIfStmt, nnkTryStmt, nnkCaseStmt,
nnkElifBranch, nnkElse, nnkElifExpr:
result = copyNimTree(n)
if n.len >= 1:
result[^1] = t(n[^1], res)
else:
if true: #n == it:
template adder(res, it) =
res.add it
result = getAst adder(res, n)
else:
result = n
when false:
let it = detectForLoopVar(body)
if it == nil: error("no for loop in body", body)
let v = newTree(nnkVarSection,
newTree(nnkIdentDefs, res, newTree(nnkBracketExpr, bindSym"seq",
newCall(bindSym"type", body)), newEmptyNode()))
result = newTree(nnkStmtListExpr, v, t(body, res), res)
#echo repr result
let stuff = collect:
var i = -1
while i < 4:
inc i
for it in data:
if it < 5 and it > 1:
it
echo stuff
echo collect(for i in 0..3: i)