123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185 |
- #
- #
- # The Nim Compiler
- # (c) Copyright 2015 Andreas Rumpf
- #
- # See the file "copying.txt", included in this
- # distribution, for details about the copyright.
- #
- ## This module does the semantic transformation of the fields* iterators.
- # included from semstmts.nim
- type
- TFieldInstCtx = object # either 'tup[i]' or 'field' is valid
- tupleType: PType # if != nil we're traversing a tuple
- tupleIndex: int
- field: PSym
- replaceByFieldName: bool
- c: PContext
- proc wrapNewScope(c: PContext, n: PNode): PNode {.inline.} =
- # use `if true` to not interfere with `break`
- # just opening scope via `openScope(c)` isn't enough,
- # a scope has to be opened in the codegen as well for reused
- # template instantiations
- let trueLit = newIntLit(c.graph, n.info, 1)
- trueLit.typ() = getSysType(c.graph, n.info, tyBool)
- result = newTreeI(nkIfStmt, n.info, newTreeI(nkElifBranch, n.info, trueLit, n))
- proc instFieldLoopBody(c: TFieldInstCtx, n: PNode, forLoop: PNode): PNode =
- if c.field != nil and isEmptyType(c.field.typ):
- result = newNode(nkEmpty)
- return
- case n.kind
- of nkEmpty..pred(nkIdent), succ(nkSym)..nkNilLit: result = copyNode(n)
- of nkIdent, nkSym:
- result = n
- let ident = considerQuotedIdent(c.c, n)
- if c.replaceByFieldName:
- if ident.id == considerQuotedIdent(c.c, forLoop[0]).id:
- let fieldName = if c.tupleType.isNil: c.field.name.s
- elif c.tupleType.n.isNil: "Field" & $c.tupleIndex
- else: c.tupleType.n[c.tupleIndex].sym.name.s
- result = newStrNode(nkStrLit, fieldName)
- return
- # other fields:
- for i in ord(c.replaceByFieldName)..<forLoop.len-2:
- if ident.id == considerQuotedIdent(c.c, forLoop[i]).id:
- var call = forLoop[^2]
- var tupl = call[i+1-ord(c.replaceByFieldName)]
- if c.field.isNil:
- result = newNodeI(nkBracketExpr, n.info)
- result.add(tupl)
- result.add(newIntNode(nkIntLit, c.tupleIndex))
- else:
- result = newNodeI(nkDotExpr, n.info)
- result.add(tupl)
- result.add(newSymNode(c.field, n.info))
- break
- else:
- if n.kind == nkContinueStmt:
- localError(c.c.config, n.info,
- "'continue' not supported in a 'fields' loop")
- result = shallowCopy(n)
- for i in 0..<n.len:
- result[i] = instFieldLoopBody(c, n[i], forLoop)
- type
- TFieldsCtx = object
- c: PContext
- m: TMagic
- proc semForObjectFields(c: TFieldsCtx, typ, forLoop, father: PNode) =
- case typ.kind
- of nkSym:
- # either 'tup[i]' or 'field' is valid
- var fc = TFieldInstCtx(
- c: c.c,
- field: typ.sym,
- replaceByFieldName: c.m == mFieldPairs
- )
- openScope(c.c)
- inc c.c.inUnrolledContext
- var body = instFieldLoopBody(fc, lastSon(forLoop), forLoop)
- # new scope for each field that codegen should know about:
- body = wrapNewScope(c.c, body)
- father.add(semStmt(c.c, body, {}))
- dec c.c.inUnrolledContext
- closeScope(c.c)
- of nkNilLit: discard
- of nkRecCase:
- let call = forLoop[^2]
- if call.len > 2:
- localError(c.c.config, forLoop.info,
- "parallel 'fields' iterator does not work for 'case' objects")
- return
- # iterate over the selector:
- semForObjectFields(c, typ[0], forLoop, father)
- # we need to generate a case statement:
- var caseStmt = newNodeI(nkCaseStmt, forLoop.info)
- # generate selector:
- var access = newNodeI(nkDotExpr, forLoop.info, 2)
- access[0] = call[1]
- access[1] = newSymNode(typ[0].sym, forLoop.info)
- caseStmt.add(semExprWithType(c.c, access))
- # copy the branches over, but replace the fields with the for loop body:
- for i in 1..<typ.len:
- var branch = copyTree(typ[i])
- branch[^1] = newNodeI(nkStmtList, forLoop.info)
- semForObjectFields(c, typ[i].lastSon, forLoop, branch[^1])
- caseStmt.add(branch)
- father.add(caseStmt)
- of nkRecList:
- for t in items(typ): semForObjectFields(c, t, forLoop, father)
- else:
- illFormedAstLocal(typ, c.c.config)
- proc semForFields(c: PContext, n: PNode, m: TMagic): PNode =
- # so that 'break' etc. work as expected, we produce
- # a 'while true: stmt; break' loop ...
- result = newNodeI(nkWhileStmt, n.info, 2)
- var trueSymbol = systemModuleSym(c.graph, getIdent(c.cache, "true"))
- if trueSymbol == nil:
- localError(c.config, n.info, "system needs: 'true'")
- trueSymbol = newSym(skUnknown, getIdent(c.cache, "true"), c.idgen, getCurrOwner(c), n.info)
- trueSymbol.typ = getSysType(c.graph, n.info, tyBool)
- result[0] = newSymNode(trueSymbol, n.info)
- var stmts = newNodeI(nkStmtList, n.info)
- result[1] = stmts
- var call = n[^2]
- if n.len-2 != call.len-1 + ord(m==mFieldPairs):
- localError(c.config, n.info, errWrongNumberOfVariables)
- return result
- const skippedTypesForFields = abstractVar - {tyTypeDesc} + tyUserTypeClasses
- var tupleTypeA = skipTypes(call[1].typ, skippedTypesForFields)
- if tupleTypeA.kind notin {tyTuple, tyObject}:
- localError(c.config, n.info, errGenerated, "no object or tuple type")
- return result
- for i in 1..<call.len:
- let calli = call[i]
- var tupleTypeB = skipTypes(calli.typ, skippedTypesForFields)
- if not sameType(tupleTypeA, tupleTypeB):
- typeMismatch(c.config, calli.info, tupleTypeA, tupleTypeB, calli)
- inc(c.p.nestedLoopCounter)
- let oldBreakInLoop = c.p.breakInLoop
- c.p.breakInLoop = true
- if tupleTypeA.kind == tyTuple:
- var loopBody = n[^1]
- for i in 0..<tupleTypeA.len:
- openScope(c)
- var fc = TFieldInstCtx(
- tupleType: tupleTypeA,
- tupleIndex: i,
- c: c,
- replaceByFieldName: m == mFieldPairs
- )
- var body = instFieldLoopBody(fc, loopBody, n)
- # new scope for each field that codegen should know about:
- body = wrapNewScope(c, body)
- inc c.inUnrolledContext
- stmts.add(semStmt(c, body, {}))
- dec c.inUnrolledContext
- closeScope(c)
- else:
- var fc = TFieldsCtx(m: m, c: c)
- var t = tupleTypeA
- while t.kind == tyObject:
- semForObjectFields(fc, t.n, n, stmts)
- if t.baseClass == nil: break
- t = skipTypes(t.baseClass, skipPtrs)
- c.p.breakInLoop = oldBreakInLoop
- dec(c.p.nestedLoopCounter)
- # for TR macros this 'while true: ...; break' loop is pretty bad, so
- # we avoid it now if we can:
- if containsNode(stmts, {nkBreakStmt}):
- var b = newNodeI(nkBreakStmt, n.info)
- b.add(newNodeI(nkEmpty, n.info))
- stmts.add(b)
- else:
- result = stmts
|