diff options
Diffstat (limited to 'compiler/semfields.nim')
-rw-r--r-- | compiler/semfields.nim | 171 |
1 files changed, 171 insertions, 0 deletions
diff --git a/compiler/semfields.nim b/compiler/semfields.nim new file mode 100644 index 000000000..874055cdc --- /dev/null +++ b/compiler/semfields.nim @@ -0,0 +1,171 @@ +# +# +# 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 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 + let body = instFieldLoopBody(fc, lastSon(forLoop), forLoop) + 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) + 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 |