semfields.nim 5.6 KB

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