semfields.nim 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  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 instFieldLoopBody(c: TFieldInstCtx, n: PNode, forLoop: PNode): PNode =
  19. if c.field != nil and isEmptyType(c.field.typ):
  20. result = newNode(nkEmpty)
  21. return
  22. case n.kind
  23. of nkEmpty..pred(nkIdent), succ(nkSym)..nkNilLit: result = n
  24. of nkIdent, nkSym:
  25. result = n
  26. let ident = considerQuotedIdent(c.c, n)
  27. if c.replaceByFieldName:
  28. if ident.id == considerQuotedIdent(c.c, forLoop[0]).id:
  29. let fieldName = if c.tupleType.isNil: c.field.name.s
  30. elif c.tupleType.n.isNil: "Field" & $c.tupleIndex
  31. else: c.tupleType.n[c.tupleIndex].sym.name.s
  32. result = newStrNode(nkStrLit, fieldName)
  33. return
  34. # other fields:
  35. for i in ord(c.replaceByFieldName)..<forLoop.len-2:
  36. if ident.id == considerQuotedIdent(c.c, forLoop[i]).id:
  37. var call = forLoop[^2]
  38. var tupl = call[i+1-ord(c.replaceByFieldName)]
  39. if c.field.isNil:
  40. result = newNodeI(nkBracketExpr, n.info)
  41. result.add(tupl)
  42. result.add(newIntNode(nkIntLit, c.tupleIndex))
  43. else:
  44. result = newNodeI(nkDotExpr, n.info)
  45. result.add(tupl)
  46. result.add(newSymNode(c.field, n.info))
  47. break
  48. else:
  49. if n.kind == nkContinueStmt:
  50. localError(c.c.config, n.info,
  51. "'continue' not supported in a 'fields' loop")
  52. result = copyNode(n)
  53. newSons(result, n.len)
  54. for i in 0..<n.len:
  55. result[i] = instFieldLoopBody(c, n[i], forLoop)
  56. type
  57. TFieldsCtx = object
  58. c: PContext
  59. m: TMagic
  60. proc semForObjectFields(c: TFieldsCtx, typ, forLoop, father: PNode) =
  61. case typ.kind
  62. of nkSym:
  63. var fc: TFieldInstCtx # either 'tup[i]' or 'field' is valid
  64. fc.c = c.c
  65. fc.field = typ.sym
  66. fc.replaceByFieldName = c.m == mFieldPairs
  67. openScope(c.c)
  68. inc c.c.inUnrolledContext
  69. let body = instFieldLoopBody(fc, lastSon(forLoop), forLoop)
  70. father.add(semStmt(c.c, body, {}))
  71. dec c.c.inUnrolledContext
  72. closeScope(c.c)
  73. of nkNilLit: discard
  74. of nkRecCase:
  75. let call = forLoop[^2]
  76. if call.len > 2:
  77. localError(c.c.config, forLoop.info,
  78. "parallel 'fields' iterator does not work for 'case' objects")
  79. return
  80. # iterate over the selector:
  81. semForObjectFields(c, typ[0], forLoop, father)
  82. # we need to generate a case statement:
  83. var caseStmt = newNodeI(nkCaseStmt, forLoop.info)
  84. # generate selector:
  85. var access = newNodeI(nkDotExpr, forLoop.info, 2)
  86. access[0] = call[1]
  87. access[1] = newSymNode(typ[0].sym, forLoop.info)
  88. caseStmt.add(semExprWithType(c.c, access))
  89. # copy the branches over, but replace the fields with the for loop body:
  90. for i in 1..<typ.len:
  91. var branch = copyTree(typ[i])
  92. branch[^1] = newNodeI(nkStmtList, forLoop.info)
  93. semForObjectFields(c, typ[i].lastSon, forLoop, branch[^1])
  94. caseStmt.add(branch)
  95. father.add(caseStmt)
  96. of nkRecList:
  97. for t in items(typ): semForObjectFields(c, t, forLoop, father)
  98. else:
  99. illFormedAstLocal(typ, c.c.config)
  100. proc semForFields(c: PContext, n: PNode, m: TMagic): PNode =
  101. # so that 'break' etc. work as expected, we produce
  102. # a 'while true: stmt; break' loop ...
  103. result = newNodeI(nkWhileStmt, n.info, 2)
  104. var trueSymbol = strTableGet(c.graph.systemModule.tab, getIdent(c.cache, "true"))
  105. if trueSymbol == nil:
  106. localError(c.config, n.info, "system needs: 'true'")
  107. trueSymbol = newSym(skUnknown, getIdent(c.cache, "true"), getCurrOwner(c), n.info)
  108. trueSymbol.typ = getSysType(c.graph, n.info, tyBool)
  109. result[0] = newSymNode(trueSymbol, n.info)
  110. var stmts = newNodeI(nkStmtList, n.info)
  111. result[1] = stmts
  112. var call = n[^2]
  113. if n.len-2 != call.len-1 + ord(m==mFieldPairs):
  114. localError(c.config, n.info, errWrongNumberOfVariables)
  115. return result
  116. const skippedTypesForFields = abstractVar - {tyTypeDesc} + tyUserTypeClasses
  117. var tupleTypeA = skipTypes(call[1].typ, skippedTypesForFields)
  118. if tupleTypeA.kind notin {tyTuple, tyObject}:
  119. localError(c.config, n.info, errGenerated, "no object or tuple type")
  120. return result
  121. for i in 1..<call.len:
  122. var tupleTypeB = skipTypes(call[i].typ, skippedTypesForFields)
  123. if not sameType(tupleTypeA, tupleTypeB):
  124. typeMismatch(c.config, call[i].info, tupleTypeA, tupleTypeB)
  125. inc(c.p.nestedLoopCounter)
  126. if tupleTypeA.kind == tyTuple:
  127. var loopBody = n[^1]
  128. for i in 0..<tupleTypeA.len:
  129. openScope(c)
  130. var fc: TFieldInstCtx
  131. fc.tupleType = tupleTypeA
  132. fc.tupleIndex = i
  133. fc.c = c
  134. fc.replaceByFieldName = m == mFieldPairs
  135. var body = instFieldLoopBody(fc, loopBody, n)
  136. inc c.inUnrolledContext
  137. stmts.add(semStmt(c, body, {}))
  138. dec c.inUnrolledContext
  139. closeScope(c)
  140. else:
  141. var fc: TFieldsCtx
  142. fc.m = m
  143. fc.c = c
  144. var t = tupleTypeA
  145. while t.kind == tyObject:
  146. semForObjectFields(fc, t.n, n, stmts)
  147. if t[0] == nil: break
  148. t = skipTypes(t[0], skipPtrs)
  149. dec(c.p.nestedLoopCounter)
  150. # for TR macros this 'while true: ...; break' loop is pretty bad, so
  151. # we avoid it now if we can:
  152. if containsNode(stmts, {nkBreakStmt}):
  153. var b = newNodeI(nkBreakStmt, n.info)
  154. b.add(newNodeI(nkEmpty, n.info))
  155. stmts.add(b)
  156. else:
  157. result = stmts