diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index c38e2f3ad6..2538636975 100755 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -301,52 +301,106 @@ proc semConst(c: PContext, n: PNode): PNode = addSon(b, copyTree(def)) addSon(result, b) -proc transfFieldLoopBody(n: PNode, forLoop: PNode, - tupleType: PType, - tupleIndex, first: int): PNode = +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 + +proc instFieldLoopBody(c: TFieldInstCtx, n: PNode, forLoop: PNode): PNode = case n.kind of nkEmpty..pred(nkIdent), succ(nkIdent)..nkNilLit: result = n of nkIdent: result = n var L = sonsLen(forLoop) - # field name: - if first > 0: + if c.replaceByFieldName: if n.ident.id == forLoop[0].ident.id: - if tupleType.n == nil: - # ugh, there are no field names: - result = newStrNode(nkStrLit, "") - else: - result = newStrNode(nkStrLit, tupleType.n.sons[tupleIndex].sym.name.s) + let fieldName = if c.tupleType.isNil: c.field.name.s + elif c.tupleType.n.isNil: "Field" & $c.tupleIndex + else: c.tupleType.n.sons[c.tupleIndex].sym.name.s + result = newStrNode(nkStrLit, fieldName) return # other fields: - for i in first..L-3: + for i in ord(c.replaceByFieldName)..L-3: if n.ident.id == forLoop[i].ident.id: var call = forLoop.sons[L-2] - var tupl = call.sons[i+1-first] - result = newNodeI(nkBracketExpr, n.info) - result.add(tupl) - result.add(newIntNode(nkIntLit, tupleIndex)) + var tupl = call.sons[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: result = copyNode(n) newSons(result, sonsLen(n)) for i in countup(0, sonsLen(n)-1): - result.sons[i] = transfFieldLoopBody(n.sons[i], forLoop, - tupleType, tupleIndex, first) + result.sons[i] = instFieldLoopBody(c, n.sons[i], forLoop) -proc semForFields(c: PContext, n: PNode, m: TMagic): PNode = - # so that 'break' etc. work as expected, we produce +type + TFieldsCtx = object + c: PContext + m: TMagic + +proc semForObjectFields(c: TFieldsCtx, typ, forLoop, father: PNode) = + case typ.kind + of nkSym: + var fc: TFieldInstCtx # either 'tup[i]' or 'field' is valid + fc.field = typ.sym + fc.replaceByFieldName = c.m == mFieldPairs + openScope(c.c.tab) + inc c.c.InUnrolledContext + let body = instFieldLoopBody(fc, lastSon(forLoop), forLoop) + father.add(SemStmt(c.c, body)) + dec c.c.InUnrolledContext + closeScope(c.c.tab) + of nkNilLit: nil + of nkRecCase: + let L = forLoop.len + let call = forLoop.sons[L-2] + if call.len > 2: + LocalError(forLoop.info, errGenerated, + "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.sons[0] = call.sons[1] + access.sons[1] = newSymNode(typ.sons[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 .. ["142", "242", "342", "442"] for i in 0..data.len-1: op(data[i]) -iterator fields*[T: tuple](x: T): TObject {. +iterator fields*[T: tuple|object](x: T): TObject {. magic: "Fields", noSideEffect.} ## iterates over every field of `x`. Warning: This really transforms ## the 'for' and unrolls the loop. The current implementation also has a bug ## that affects symbol binding in the loop body. -iterator fields*[S: tuple, T: tuple](x: S, y: T): tuple[a, b: expr] {. +iterator fields*[S:tuple|object, T:tuple|object](x: S, y: T): tuple[a,b: expr] {. magic: "Fields", noSideEffect.} ## iterates over every field of `x` and `y`. ## Warning: This is really transforms the 'for' and unrolls the loop. ## The current implementation also has a bug that affects symbol binding ## in the loop body. -iterator fieldPairs*[T: tuple](x: T): TObject {. +iterator fieldPairs*[T: tuple|object](x: T): TObject {. magic: "FieldPairs", noSideEffect.} ## iterates over every field of `x`. Warning: This really transforms ## the 'for' and unrolls the loop. The current implementation also has a bug ## that affects symbol binding in the loop body. -iterator fieldPairs*[S: tuple, T: tuple](x: S, y: T): tuple[a, b: expr] {. +iterator fieldPairs*[S: tuple|object, T: tuple|object](x: S, y: T): tuple[ + a, b: expr] {. magic: "FieldPairs", noSideEffect.} ## iterates over every field of `x` and `y`. ## Warning: This really transforms the 'for' and unrolls the loop. diff --git a/tests/run/tfielditerator2.nim b/tests/run/tfielditerator2.nim new file mode 100644 index 0000000000..76fa568f2a --- /dev/null +++ b/tests/run/tfielditerator2.nim @@ -0,0 +1,64 @@ +discard """ + output: ''' +a char: true +a char: false +an int: 5 +an int: 6 +a string: abc +false +true +true +false +true +a: a +b: b +x: 5 +y: 6 +z: abc +myDisc: enC +c: Z +enC +Z +''' +""" + +type + TMyObj = object + a, b: char + x, y: int + z: string + + TEnum = enum enA, enB, enC + TMyCaseObj = object + case myDisc: TEnum + of enA: a: int + of enB: b: string + of enC: c: char + +proc p(x: char) = echo "a char: ", x <= 'a' +proc p(x: int) = echo "an int: ", x +proc p(x: string) = echo "a string: ", x + +proc myobj(a, b: char, x, y: int, z: string): TMyObj = + result.a = a; result.b = b; result.x = x; result.y = y; result.z = z + +var x = myobj('a', 'b', 5, 6, "abc") +var y = myobj('A', 'b', 5, 9, "abc") + +for f in fields(x): + p f + +for a, b in fields(x, y): + echo a == b + +for key, val in fieldPairs(x): + echo key, ": ", val + +var co: TMyCaseObj +co.myDisc = enC +co.c = 'Z' +for key, val in fieldPairs(co): + echo key, ": ", val + +for val in fields(co): + echo val diff --git a/web/news.txt b/web/news.txt index efe215f93a..3ec1ee0bf3 100755 --- a/web/news.txt +++ b/web/news.txt @@ -26,6 +26,8 @@ Library Additions - Added ``system.unsafeNew`` to support hacky variable length objects. - There is a new experimental mark&sweep GC which can be faster (or much slower) than the default GC. Enable with ``--gc:markAndSweep``. +- ``system.fields`` and ``system.fieldPairs`` support ``object`` too; they + used to only suppor tuples. Changes affecting backwards compatibility