# # # The Nimrod Compiler # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # # This include file implements lambda lifting for the transformator. # included from transf.nim const declarativeDefs = {nkProcDef, nkMethodDef, nkIteratorDef, nkConverterDef} procDefs = nkLambdaKinds + declarativeDefs proc indirectAccess(a, b: PSym, info: TLineInfo): PNode = # returns a[].b as a node let x = newSymNode(a) var deref = newNodeI(nkHiddenDeref, info) deref.typ = x.typ.sons[0] let field = getSymFromList(deref.typ.n, b.name) addSon(deref, x) result = newNodeI(nkDotExpr, info) addSon(result, deref) addSon(result, newSymNode(field)) result.typ = field.typ type TCapture = seq[PSym] proc Capture(cap: var TCapture, s: PSym) = for x in cap: if x.name.id == s.name.id: return cap.add(s) proc captureToTuple(cap: TCapture, owner: PSym): PType = result = newType(tyTuple, owner) result.n = newNodeI(nkRecList, owner.info) for s in cap: var field = newSym(skField, s.name, s.owner) let typ = s.typ field.typ = typ field.position = sonsLen(result) addSon(result.n, newSymNode(field)) addSon(result, typ) proc interestingVar(s: PSym): bool {.inline.} = result = s.kind in {skVar, skLet, skTemp, skForVar, skParam, skResult} and sfGlobal notin s.flags proc gatherVars(c: PTransf, n: PNode, outerProc: PSym, cap: var TCapture) = # gather used vars for closure generation into 'cap' case n.kind of nkSym: var s = n.sym if interestingVar(s) and outerProc.id == s.owner.id: #echo "captured: ", s.name.s Capture(cap, s) of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit: nil else: for i in countup(0, sonsLen(n) - 1): gatherVars(c, n.sons[i], outerProc, cap) proc replaceVars(c: PTransf, n: PNode, outerProc, env: PSym) = for i in countup(0, safeLen(n) - 1): let a = n.sons[i] if a.kind == nkSym: let s = a.sym if interestingVar(s) and outerProc == s.owner: # access through the closure param: n.sons[i] = indirectAccess(env, s, n.info) else: replaceVars(c, a, outerProc, env) proc addHiddenParam(routine: PSym, param: PSym) = var params = routine.ast.sons[paramsPos] param.position = params.len addSon(params, newSymNode(param)) #echo "produced environment: ", param.id, " for ", routine.name.s proc isInnerProc(s, outerProc: PSym): bool {.inline.} = result = s.kind in {skProc, skMacro, skIterator, skMethod, skConverter} and s.owner == outerProc and not isGenericRoutine(s) #s.typ.callConv == ccClosure proc searchForInnerProcs(c: PTransf, n: PNode, outerProc: PSym, cap: var TCapture) = case n.kind of nkSym: if isInnerProc(n.sym, outerProc): gatherVars(c, n.sym.getBody, outerProc, cap) of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit: nil else: for i in 0..