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
commit 132eb31e32
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 163 additions and 81 deletions

View file

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

View file

@ -537,7 +537,7 @@ proc parsePar(p: var TParser): PNode =
flexComment(p, result) flexComment(p, result)
if p.tok.tokType in {tkDiscard, tkInclude, tkIf, tkWhile, tkCase, if p.tok.tokType in {tkDiscard, tkInclude, tkIf, tkWhile, tkCase,
tkTry, tkDefer, tkFinally, tkExcept, tkFor, tkBlock, tkTry, tkDefer, tkFinally, tkExcept, tkFor, tkBlock,
tkConst, tkLet, tkWhen, tkVar, tkConst, tkLet, tkWhen, tkVar, tkFor,
tkMixin}: tkMixin}:
# XXX 'bind' used to be an expression, so we exclude it here; # XXX 'bind' used to be an expression, so we exclude it here;
# tests/reject/tbind2 fails otherwise. # tests/reject/tbind2 fails otherwise.
@ -1112,7 +1112,7 @@ proc parseProcExpr(p: var TParser; isExpr: bool; kind: TNodeKind): PNode =
proc isExprStart(p: TParser): bool = proc isExprStart(p: TParser): bool =
case p.tok.tokType 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, tkProc, tkFunc, tkIterator, tkBind, tkBuiltInMagics,
tkParLe, tkBracketLe, tkCurlyLe, tkIntLit..tkCharLit, tkVar, tkRef, tkPtr, tkParLe, tkBracketLe, tkCurlyLe, tkIntLit..tkCharLit, tkVar, tkRef, tkPtr,
tkTuple, tkObject, tkWhen, tkCase, tkOut: tkTuple, tkObject, tkWhen, tkCase, tkOut:
@ -1152,16 +1152,35 @@ proc parseTypeDescKAux(p: var TParser, kind: TNodeKind,
result.addSon list result.addSon list
parseSymbolList(p, 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 = proc parseExpr(p: var TParser): PNode =
#| expr = (blockExpr #| expr = (blockExpr
#| | ifExpr #| | ifExpr
#| | whenExpr #| | whenExpr
#| | caseExpr #| | caseExpr
#| | forExpr
#| | tryExpr) #| | tryExpr)
#| / simpleExpr #| / simpleExpr
case p.tok.tokType: case p.tok.tokType:
of tkBlock: result = parseBlock(p) of tkBlock: result = parseBlock(p)
of tkIf: result = parseIfExpr(p, nkIfExpr) of tkIf: result = parseIfExpr(p, nkIfExpr)
of tkFor: result = parseFor(p)
of tkWhen: result = parseIfExpr(p, nkWhenExpr) of tkWhen: result = parseIfExpr(p, nkWhenExpr)
of tkCase: result = parseCase(p) of tkCase: result = parseCase(p)
of tkTry: result = parseTry(p, isExpr=true) of tkTry: result = parseTry(p, isExpr=true)
@ -1568,22 +1587,6 @@ proc parseExceptBlock(p: var TParser, kind: TNodeKind): PNode =
colcom(p, result) colcom(p, result)
addSon(result, parseStmt(p)) 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 = proc parseBlock(p: var TParser): PNode =
#| blockStmt = 'block' symbol? colcom stmt #| blockStmt = 'block' symbol? colcom stmt
#| blockExpr = '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 semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode
proc semTypeNode(c: PContext, n: PNode, prev: PType): PType 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 semOpAux(c: PContext, n: PNode)
proc semParamList(c: PContext, n, genericParams: PNode, s: PSym) proc semParamList(c: PContext, n, genericParams: PNode, s: PSym)
proc addParams(c: PContext, n: PNode, kind: TSymKind) proc addParams(c: PContext, n: PNode, kind: TSymKind)
@ -399,7 +399,7 @@ proc semAfterMacroCall(c: PContext, call, macroResult: PNode,
excl(result.flags, nfSem) excl(result.flags, nfSem)
#resetSemFlag n #resetSemFlag n
if s.typ.sons[0] == nil: if s.typ.sons[0] == nil:
result = semStmt(c, result) result = semStmt(c, result, flags)
else: else:
case s.typ.sons[0].kind case s.typ.sons[0].kind
of tyExpr: of tyExpr:
@ -408,7 +408,7 @@ proc semAfterMacroCall(c: PContext, call, macroResult: PNode,
# semExprWithType(c, result) # semExprWithType(c, result)
result = semExpr(c, result, flags) result = semExpr(c, result, flags)
of tyStmt: of tyStmt:
result = semStmt(c, result) result = semStmt(c, result, flags)
of tyTypeDesc: of tyTypeDesc:
if result.kind == nkStmtList: result.kind = nkStmtListType if result.kind == nkStmtList: result.kind = nkStmtListType
var typ = semTypeNode(c, result, nil) var typ = semTypeNode(c, result, nil)
@ -557,7 +557,7 @@ proc semStmtAndGenerateGenerics(c: PContext, n: PNode): PNode =
result = semAllTypeSections(c, n) result = semAllTypeSections(c, n)
else: else:
result = n result = n
result = semStmt(c, result) result = semStmt(c, result, {})
when false: when false:
# Code generators are lazy now and can deal with undeclared procs, so these # Code generators are lazy now and can deal with undeclared procs, so these
# steps are not required anymore and actually harmful for the upcoming # 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 let isPush = hintExtendedContext in c.config.notes
if isPush: pushInfoContext(c.config, n.info) if isPush: pushInfoContext(c.config, n.info)
result = semExpr(c, n, {efWantStmt}) result = semExpr(c, n, {efWantStmt})
discardCheck(c, result) discardCheck(c, result, {})
if isPush: popInfoContext(c.config) if isPush: popInfoContext(c.config)
proc isTypeExpr(n: PNode): bool = 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 # unfortunately we need to rewrite ``(x, y) = foo()`` already here so
# that overloading of the assignment operator still works. Usually we # that overloading of the assignment operator still works. Usually we
# prefer to do these rewritings in transf.nim: # 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: else:
a = semExprWithType(c, a, {efLValue}) a = semExprWithType(c, a, {efLValue})
else: else:
@ -1614,7 +1614,7 @@ proc semProcBody(c: PContext, n: PNode): PNode =
a.sons[1] = result a.sons[1] = result
result = semAsgn(c, a) result = semAsgn(c, a)
else: else:
discardCheck(c, result) discardCheck(c, result, {})
if c.p.owner.kind notin {skMacro, skTemplate} and if c.p.owner.kind notin {skMacro, skTemplate} and
c.p.resultSym != nil and c.p.resultSym.typ.isMetaType: 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 var x = n.lastSon
if x.kind == nkDo: x = x.sons[bodyPos] if x.kind == nkDo: x = x.sons[bodyPos]
inc c.inParallelStmt inc c.inParallelStmt
result.sons[1] = semStmt(c, x) result.sons[1] = semStmt(c, x, {})
dec c.inParallelStmt dec c.inParallelStmt
of mSpawn: of mSpawn:
result = setMs(n, s) result = setMs(n, s)
@ -2241,7 +2241,7 @@ proc isTupleType(n: PNode): bool =
include semobjconstr include semobjconstr
proc semBlock(c: PContext, n: PNode): PNode = proc semBlock(c: PContext, n: PNode; flags: TExprFlags): PNode =
result = n result = n
inc(c.p.nestedBlockCounter) inc(c.p.nestedBlockCounter)
checkSonsLen(n, 2, c.config) 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) n.sons[0] = newSymNode(labl, n.sons[0].info)
suggestSym(c.config, n.sons[0].info, labl, c.graph.usageSym) suggestSym(c.config, n.sons[0].info, labl, c.graph.usageSym)
styleCheckDef(c.config, labl) 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 n.typ = n.sons[1].typ
if isEmptyType(n.typ): n.kind = nkBlockStmt if isEmptyType(n.typ): n.kind = nkBlockStmt
else: n.kind = nkBlockExpr else: n.kind = nkBlockExpr
@ -2498,7 +2498,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
checkSonsLen(n, 1, c.config) checkSonsLen(n, 1, c.config)
n.sons[0] = semExpr(c, n.sons[0], flags) n.sons[0] = semExpr(c, n.sons[0], flags)
of nkCast: result = semCast(c, n) 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: of nkHiddenStdConv, nkHiddenSubConv, nkConv, nkHiddenCallConv:
checkSonsLen(n, 2, c.config) checkSonsLen(n, 2, c.config)
considerGenSyms(c, n) considerGenSyms(c, n)
@ -2519,7 +2519,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
discard discard
of nkStaticExpr: result = semStaticExpr(c, n[0]) of nkStaticExpr: result = semStaticExpr(c, n[0])
of nkAsgn: result = semAsgn(c, n) 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 nkStmtList, nkStmtListExpr: result = semStmtList(c, n, flags)
of nkRaiseStmt: result = semRaise(c, n) of nkRaiseStmt: result = semRaise(c, n)
of nkVarSection: result = semVarOrLet(c, n, skVar) 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 nkConstSection: result = semConst(c, n)
of nkTypeSection: result = semTypeSection(c, n) of nkTypeSection: result = semTypeSection(c, n)
of nkDiscardStmt: result = semDiscard(c, n) of nkDiscardStmt: result = semDiscard(c, n)
of nkWhileStmt: result = semWhile(c, n) of nkWhileStmt: result = semWhile(c, n, flags)
of nkTryStmt: result = semTry(c, n) of nkTryStmt: result = semTry(c, n, flags)
of nkBreakStmt, nkContinueStmt: result = semBreakOrContinue(c, n) of nkBreakStmt, nkContinueStmt: result = semBreakOrContinue(c, n)
of nkForStmt, nkParForStmt: result = semFor(c, n) of nkForStmt, nkParForStmt: result = semFor(c, n, flags)
of nkCaseStmt: result = semCase(c, n) of nkCaseStmt: result = semCase(c, n, flags)
of nkReturnStmt: result = semReturn(c, n) of nkReturnStmt: result = semReturn(c, n)
of nkUsingStmt: result = semUsing(c, n) of nkUsingStmt: result = semUsing(c, n)
of nkAsmStmt: result = semAsm(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) openScope(c.c)
inc c.c.inUnrolledContext inc c.c.inUnrolledContext
let body = instFieldLoopBody(fc, lastSon(forLoop), forLoop) let body = instFieldLoopBody(fc, lastSon(forLoop), forLoop)
father.add(semStmt(c.c, body)) father.add(semStmt(c.c, body, {}))
dec c.c.inUnrolledContext dec c.c.inUnrolledContext
closeScope(c.c) closeScope(c.c)
of nkNilLit: discard of nkNilLit: discard
@ -145,7 +145,7 @@ proc semForFields(c: PContext, n: PNode, m: TMagic): PNode =
fc.replaceByFieldName = m == mFieldPairs fc.replaceByFieldName = m == mFieldPairs
var body = instFieldLoopBody(fc, loopBody, n) var body = instFieldLoopBody(fc, loopBody, n)
inc c.inUnrolledContext inc c.inUnrolledContext
stmts.add(semStmt(c, body)) stmts.add(semStmt(c, body, {}))
dec c.inUnrolledContext dec c.inUnrolledContext
closeScope(c) closeScope(c)
else: else:

View file

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

View file

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