summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorAraq <rumpf_a@web.de>2013-02-18 17:23:26 +0100
committerAraq <rumpf_a@web.de>2013-02-18 17:23:26 +0100
commitd15981adbcfe507f58f3cc6167272d8af363bbf4 (patch)
treebac2792cc435641353050635bc6d9a8a84733732
parent8e9b39084c4ed378ef98d3e2d6cc958f976862b4 (diff)
downloadNim-d15981adbcfe507f58f3cc6167272d8af363bbf4.tar.gz
system.fields|fieldPairs for objects
-rwxr-xr-xcompiler/semstmts.nim129
-rwxr-xr-xcompiler/types.nim3
-rwxr-xr-xlib/system.nim9
-rw-r--r--tests/run/tfielditerator2.nim64
-rwxr-xr-xweb/news.txt2
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