diff options
author | Araq <rumpf_a@web.de> | 2013-02-18 17:23:26 +0100 |
---|---|---|
committer | Araq <rumpf_a@web.de> | 2013-02-18 17:23:26 +0100 |
commit | d15981adbcfe507f58f3cc6167272d8af363bbf4 (patch) | |
tree | bac2792cc435641353050635bc6d9a8a84733732 | |
parent | 8e9b39084c4ed378ef98d3e2d6cc958f976862b4 (diff) | |
download | Nim-d15981adbcfe507f58f3cc6167272d8af363bbf4.tar.gz |
system.fields|fieldPairs for objects
-rwxr-xr-x | compiler/semstmts.nim | 129 | ||||
-rwxr-xr-x | compiler/types.nim | 3 | ||||
-rwxr-xr-x | lib/system.nim | 9 | ||||
-rw-r--r-- | tests/run/tfielditerator2.nim | 64 | ||||
-rwxr-xr-x | web/news.txt | 2 |
5 files changed, 170 insertions, 37 deletions
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index c38e2f3ad..253863697 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) + +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 .. <typ.len: + var branch = copyTree(typ[i]) + let L = branch.len + branch.sons[L-1] = newNodeI(nkStmtList, forLoop.info) + semForObjectFields(c, typ[i].lastSon, forLoop, branch[L-1]) + caseStmt.add(branch) + father.add(caseStmt) + of nkRecList: + for t in items(typ): semForObjectFields(c, t, forLoop, father) + else: + illFormedAst(typ) -proc semForFields(c: PContext, n: PNode, m: TMagic): PNode = - # so that 'break' etc. work as expected, we produce +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) + result = newNodeI(nkWhileStmt, n.info, 2) var trueSymbol = StrTableGet(magicsys.systemModule.Tab, getIdent"true") if trueSymbol == nil: LocalError(n.info, errSystemNeeds, "true") trueSymbol = newSym(skUnknown, getIdent"true", getCurrOwner(), n.info) trueSymbol.typ = getSysType(tyBool) - result.add(newSymNode(trueSymbol, n.info)) + result.sons[0] = newSymNode(trueSymbol, n.info) var stmts = newNodeI(nkStmtList, n.info) - result.add(stmts) + result.sons[1] = stmts var length = sonsLen(n) var call = n.sons[length-2] @@ -355,22 +409,33 @@ proc semForFields(c: PContext, n: PNode, m: TMagic): PNode = return result var tupleTypeA = skipTypes(call.sons[1].typ, abstractVar) - if tupleTypeA.kind != tyTuple: InternalError(n.info, "no tuple type!") + if tupleTypeA.kind notin {tyTuple, tyObject}: + localError(n.info, errGenerated, "no object or tuple type") + return result for i in 1..call.len-1: var tupleTypeB = skipTypes(call.sons[i].typ, abstractVar) if not SameType(tupleTypeA, tupleTypeB): typeMismatch(call.sons[i], tupleTypeA, tupleTypeB) Inc(c.p.nestedLoopCounter) - var loopBody = n.sons[length-1] - for i in 0..sonsLen(tupleTypeA)-1: - openScope(c.tab) - var body = transfFieldLoopBody(loopBody, n, tupleTypeA, i, - ord(m==mFieldPairs)) - inc c.InUnrolledContext - stmts.add(SemStmt(c, body)) - dec c.InUnrolledContext - closeScope(c.tab) + if tupleTypeA.kind == tyTuple: + var loopBody = n.sons[length-1] + for i in 0..sonsLen(tupleTypeA)-1: + openScope(c.tab) + var fc: TFieldInstCtx + fc.tupleType = tupleTypeA + fc.tupleIndex = i + fc.replaceByFieldName = m == mFieldPairs + var body = instFieldLoopBody(fc, loopBody, n) + inc c.InUnrolledContext + stmts.add(SemStmt(c, body)) + dec c.InUnrolledContext + closeScope(c.tab) + else: + var fc: TFieldsCtx + fc.m = m + fc.c = c + semForObjectFields(fc, tupleTypeA.n, n, stmts) Dec(c.p.nestedLoopCounter) var b = newNodeI(nkBreakStmt, n.info) b.add(ast.emptyNode) diff --git a/compiler/types.nim b/compiler/types.nim index 998ba43d2..1c660958c 100755 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -938,7 +938,8 @@ proc matchTypeClass*(bindings: var TIdTable, typeClass, t: PType): bool = of tyTypeClass: match = matchTypeClass(bindings, req, t) else: nil - elif t.kind in {tyObject}: + elif t.kind in {tyObject} and req.len != 0: + # empty 'object' is fine as constraint in a type class match = sameType(t, req) if tfAny in typeClass.flags: diff --git a/lib/system.nim b/lib/system.nim index ea765644c..a17b2c97d 100755 --- a/lib/system.nim +++ b/lib/system.nim @@ -1508,23 +1508,24 @@ proc map*[T](data: var openArray[T], op: proc (x: var T) {.closure.}) = ## # --> ["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 000000000..76fa568f2 --- /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 efe215f93..3ec1ee0bf 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 |