semfields.nim 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. #
  2. #
  3. # The Nim Compiler
  4. # (c) Copyright 2015 Andreas Rumpf
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. ## This module does the semantic transformation of the fields* iterators.
  10. # included from semstmts.nim
  11. type
  12. TFieldInstCtx = object # either 'tup[i]' or 'field' is valid
  13. tupleType: PType # if != nil we're traversing a tuple
  14. tupleIndex: int
  15. field: PSym
  16. replaceByFieldName: bool
  17. c: PContext
  18. proc wrapNewScope(c: PContext, n: PNode): PNode {.inline.} =
  19. # use `if true` to not interfere with `break`
  20. # just opening scope via `openScope(c)` isn't enough,
  21. # a scope has to be opened in the codegen as well for reused
  22. # template instantiations
  23. let trueLit = newIntLit(c.graph, n.info, 1)
  24. trueLit.typ() = getSysType(c.graph, n.info, tyBool)
  25. result = newTreeI(nkIfStmt, n.info, newTreeI(nkElifBranch, n.info, trueLit, n))
  26. proc instFieldLoopBody(c: TFieldInstCtx, n: PNode, forLoop: PNode): PNode =
  27. if c.field != nil and isEmptyType(c.field.typ):
  28. result = newNode(nkEmpty)
  29. return
  30. case n.kind
  31. of nkEmpty..pred(nkIdent), succ(nkSym)..nkNilLit: result = copyNode(n)
  32. of nkIdent, nkSym:
  33. result = n
  34. let ident = considerQuotedIdent(c.c, n)
  35. if c.replaceByFieldName and
  36. ident.id != ord(wUnderscore):
  37. if ident.id == considerQuotedIdent(c.c, forLoop[0]).id:
  38. let fieldName = if c.tupleType.isNil: c.field.name.s
  39. elif c.tupleType.n.isNil: "Field" & $c.tupleIndex
  40. else: c.tupleType.n[c.tupleIndex].sym.name.s
  41. result = newStrNode(nkStrLit, fieldName)
  42. return
  43. # other fields:
  44. for i in ord(c.replaceByFieldName)..<forLoop.len-2:
  45. if ident.id == considerQuotedIdent(c.c, forLoop[i]).id and
  46. ident.id != ord(wUnderscore):
  47. var call = forLoop[^2]
  48. var tupl = call[i+1-ord(c.replaceByFieldName)]
  49. if c.field.isNil:
  50. result = newNodeI(nkBracketExpr, n.info)
  51. result.add(tupl)
  52. result.add(newIntNode(nkIntLit, c.tupleIndex))
  53. else:
  54. result = newNodeI(nkDotExpr, n.info)
  55. result.add(tupl)
  56. result.add(newSymNode(c.field, n.info))
  57. break
  58. else:
  59. if n.kind == nkContinueStmt:
  60. localError(c.c.config, n.info,
  61. "'continue' not supported in a 'fields' loop")
  62. result = shallowCopy(n)
  63. for i in 0..<n.len:
  64. result[i] = instFieldLoopBody(c, n[i], forLoop)
  65. type
  66. TFieldsCtx = object
  67. c: PContext
  68. m: TMagic
  69. proc semForObjectFields(c: TFieldsCtx, typ, forLoop, father: PNode) =
  70. case typ.kind
  71. of nkSym:
  72. # either 'tup[i]' or 'field' is valid
  73. var fc = TFieldInstCtx(
  74. c: c.c,
  75. field: typ.sym,
  76. replaceByFieldName: c.m == mFieldPairs
  77. )
  78. openScope(c.c)
  79. inc c.c.inUnrolledContext
  80. var body = instFieldLoopBody(fc, lastSon(forLoop), forLoop)
  81. # new scope for each field that codegen should know about:
  82. body = wrapNewScope(c.c, body)
  83. father.add(semStmt(c.c, body, {}))
  84. dec c.c.inUnrolledContext
  85. closeScope(c.c)
  86. of nkNilLit: discard
  87. of nkRecCase:
  88. let call = forLoop[^2]
  89. if call.len > 2:
  90. localError(c.c.config, forLoop.info,
  91. "parallel 'fields' iterator does not work for 'case' objects")
  92. return
  93. # iterate over the selector:
  94. semForObjectFields(c, typ[0], forLoop, father)
  95. # we need to generate a case statement:
  96. var caseStmt = newNodeI(nkCaseStmt, forLoop.info)
  97. # generate selector:
  98. var access = newNodeI(nkDotExpr, forLoop.info, 2)
  99. access[0] = call[1]
  100. access[1] = newSymNode(typ[0].sym, forLoop.info)
  101. caseStmt.add(semExprWithType(c.c, access))
  102. # copy the branches over, but replace the fields with the for loop body:
  103. for i in 1..<typ.len:
  104. var branch = copyTree(typ[i])
  105. branch[^1] = newNodeI(nkStmtList, forLoop.info)
  106. semForObjectFields(c, typ[i].lastSon, forLoop, branch[^1])
  107. caseStmt.add(branch)
  108. father.add(caseStmt)
  109. of nkRecList:
  110. for t in items(typ): semForObjectFields(c, t, forLoop, father)
  111. else:
  112. illFormedAstLocal(typ, c.c.config)
  113. proc semForFields(c: PContext, n: PNode, m: TMagic): PNode =
  114. # so that 'break' etc. work as expected, we produce
  115. # a 'while true: stmt; break' loop ...
  116. result = newNodeI(nkWhileStmt, n.info, 2)
  117. var trueSymbol = systemModuleSym(c.graph, getIdent(c.cache, "true"))
  118. if trueSymbol == nil:
  119. localError(c.config, n.info, "system needs: 'true'")
  120. trueSymbol = newSym(skUnknown, getIdent(c.cache, "true"), c.idgen, getCurrOwner(c), n.info)
  121. trueSymbol.typ = getSysType(c.graph, n.info, tyBool)
  122. result[0] = newSymNode(trueSymbol, n.info)
  123. var stmts = newNodeI(nkStmtList, n.info)
  124. result[1] = stmts
  125. var call = n[^2]
  126. if n.len-2 != call.len-1 + ord(m==mFieldPairs):
  127. localError(c.config, n.info, errWrongNumberOfVariables)
  128. return result
  129. const skippedTypesForFields = abstractVar - {tyTypeDesc} + tyUserTypeClasses
  130. var tupleTypeA = skipTypes(call[1].typ, skippedTypesForFields)
  131. if tupleTypeA.kind notin {tyTuple, tyObject}:
  132. localError(c.config, n.info, errGenerated, "no object or tuple type")
  133. return result
  134. for i in 1..<call.len:
  135. let calli = call[i]
  136. var tupleTypeB = skipTypes(calli.typ, skippedTypesForFields)
  137. if not sameType(tupleTypeA, tupleTypeB):
  138. typeMismatch(c.config, calli.info, tupleTypeA, tupleTypeB, calli)
  139. inc(c.p.nestedLoopCounter)
  140. let oldBreakInLoop = c.p.breakInLoop
  141. c.p.breakInLoop = true
  142. if tupleTypeA.kind == tyTuple:
  143. var loopBody = n[^1]
  144. for i in 0..<tupleTypeA.len:
  145. openScope(c)
  146. var fc = TFieldInstCtx(
  147. tupleType: tupleTypeA,
  148. tupleIndex: i,
  149. c: c,
  150. replaceByFieldName: m == mFieldPairs
  151. )
  152. var body = instFieldLoopBody(fc, loopBody, n)
  153. # new scope for each field that codegen should know about:
  154. body = wrapNewScope(c, body)
  155. inc c.inUnrolledContext
  156. stmts.add(semStmt(c, body, {}))
  157. dec c.inUnrolledContext
  158. closeScope(c)
  159. else:
  160. var fc = TFieldsCtx(m: m, c: c)
  161. var t = tupleTypeA
  162. while t.kind == tyObject:
  163. semForObjectFields(fc, t.n, n, stmts)
  164. if t.baseClass == nil: break
  165. t = skipTypes(t.baseClass, skipPtrs)
  166. c.p.breakInLoop = oldBreakInLoop
  167. dec(c.p.nestedLoopCounter)
  168. # for TR macros this 'while true: ...; break' loop is pretty bad, so
  169. # we avoid it now if we can:
  170. if containsNode(stmts, {nkBreakStmt}):
  171. var b = newNodeI(nkBreakStmt, n.info)
  172. b.add(newNodeI(nkEmpty, n.info))
  173. stmts.add(b)
  174. else:
  175. result = stmts