system.fields|fieldPairs for objects

This commit is contained in:
Araq 2013-02-18 17:23:26 +01:00
commit d15981adbc
5 changed files with 170 additions and 37 deletions

View file

@ -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 .. <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
# 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)