# # # The Nimrod Compiler # (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # ## This file implements the new evaluation engine for Nimrod code. ## An instruction is 1-2 int32s in memory, it is a register based VM. import ast except getstr import strutils, astalgo, msgs, vmdef, vmgen, nimsets, types, passes, unsigned, parser, vmdeps, idents, trees, renderer, options from semfold import leValueConv, ordinalValToString when hasFFI: import evalffi type PStackFrame* = ref TStackFrame TStackFrame* = object prc: PSym # current prc; proc that is evaluated slots: TNodeSeq # parameters passed to the proc + locals; # parameters come first next: PStackFrame # for stacking comesFrom: int safePoints: seq[int] # used for exception handling # XXX 'break' should perform cleanup actions # What does the C backend do for it? proc stackTraceAux(c: PCtx; x: PStackFrame; pc: int) = if x != nil: stackTraceAux(c, x.next, x.comesFrom) var info = c.debug[pc] # we now use the same format as in system/except.nim var s = toFilename(info) var line = toLineNumber(info) if line > 0: add(s, '(') add(s, $line) add(s, ')') if x.prc != nil: for k in 1..max(1, 25-s.len): add(s, ' ') add(s, x.prc.name.s) MsgWriteln(s) proc stackTrace(c: PCtx, tos: PStackFrame, pc: int, msg: TMsgKind, arg = "") = MsgWriteln("stack trace: (most recent call last)") stackTraceAux(c, tos, pc) LocalError(c.debug[pc], msg, arg) proc bailOut(c: PCtx; tos: PStackFrame) = stackTrace(c, tos, c.exceptionInstr, errUnhandledExceptionX, c.currentExceptionA.sons[2].strVal) when not defined(nimComputedGoto): {.pragma: computedGoto.} template inc(pc: ptr TInstr, diff = 1) = inc cast[TAddress](pc), TInstr.sizeof * diff proc myreset(n: PNode) = when defined(system.reset): var oldInfo = n.info reset(n[]) n.info = oldInfo proc skipMeta(n: PNode): PNode = (if n.kind != nkMetaNode: n else: n.sons[0]) proc setMeta(n, child: PNode) = assert n.kind == nkMetaNode let child = child.skipMeta if n.sons.isNil: n.sons = @[child] else: n.sons[0] = child proc uast(n: PNode): PNode {.inline.} = # "underlying ast" assert n.kind == nkMetaNode n.sons[0] template ensureKind(k: expr) {.immediate, dirty.} = if regs[ra].kind != k: myreset(regs[ra]) regs[ra].kind = k template decodeB(k: expr) {.immediate, dirty.} = let rb = instr.regB ensureKind(k) template decodeBC(k: expr) {.immediate, dirty.} = let rb = instr.regB let rc = instr.regC ensureKind(k) template declBC() {.immediate, dirty.} = let rb = instr.regB let rc = instr.regC template decodeBImm(k: expr) {.immediate, dirty.} = let rb = instr.regB let imm = instr.regC - byteExcess ensureKind(k) template decodeBx(k: expr) {.immediate, dirty.} = let rbx = instr.regBx - wordExcess ensureKind(k) template move(a, b: expr) = system.shallowCopy(a, b) # XXX fix minor 'shallowCopy' overloading bug in compiler proc moveConst(x, y: PNode) = if x.kind != y.kind: myreset(x) x.kind = y.kind x.typ = y.typ case x.kind of nkCharLit..nkInt64Lit: x.intVal = y.intVal of nkFloatLit..nkFloat64Lit: x.floatVal = y.floatVal of nkStrLit..nkTripleStrLit: move(x.strVal, y.strVal) of nkIdent: x.ident = y.ident of nkSym: x.sym = y.sym of nkMetaNode: if x.sons.isNil: x.sons = @[y.sons[0]] else: x.sons[0] = y.sons[0] else: if x.kind notin {nkEmpty..nkNilLit}: move(x.sons, y.sons) # this seems to be the best way to model the reference semantics # of PNimrodNode: template asgnRef(x, y: expr) = moveConst(x, y) proc copyValue(src: PNode): PNode = if src == nil or nfIsRef in src.flags: return src result = newNode(src.kind) result.info = src.info result.typ = src.typ result.flags = src.flags * PersistentNodeFlags when defined(useNodeIds): if result.id == nodeIdToDebug: echo "COMES FROM ", src.id case src.Kind of nkCharLit..nkUInt64Lit: result.intVal = src.intVal of nkFloatLit..nkFloat128Lit: result.floatVal = src.floatVal of nkSym: result.sym = src.sym of nkIdent: result.ident = src.ident of nkStrLit..nkTripleStrLit: result.strVal = src.strVal else: newSeq(result.sons, sonsLen(src)) for i in countup(0, sonsLen(src) - 1): result.sons[i] = copyValue(src.sons[i]) proc asgnComplex(x, y: PNode) = if x.kind != y.kind: myreset(x) x.kind = y.kind x.typ = y.typ case x.kind of nkCharLit..nkInt64Lit: x.intVal = y.intVal of nkFloatLit..nkFloat64Lit: x.floatVal = y.floatVal of nkStrLit..nkTripleStrLit: x.strVal = y.strVal of nkIdent: x.ident = y.ident of nkSym: x.sym = y.sym of nkMetaNode: if x.sons.isNil: x.sons = @[y.sons[0]] else: x.sons[0] = y.sons[0] else: if x.kind notin {nkEmpty..nkNilLit}: let y = y.copyValue for i in countup(0, sonsLen(y) - 1): addSon(x, y.sons[i]) template getstr(a: expr): expr = (if a.kind in {nkStrLit..nkTripleStrLit}: a.strVal else: $chr(int(a.intVal))) proc pushSafePoint(f: PStackFrame; pc: int) = if f.safePoints.isNil: f.safePoints = @[] f.safePoints.add(pc) proc popSafePoint(f: PStackFrame) = discard f.safePoints.pop() proc cleanUpOnException(c: PCtx; tos: PStackFrame; regs: TNodeSeq): int = let raisedType = c.currentExceptionA.typ.skipTypes(abstractPtrs) var f = tos while true: while f.safePoints.isNil or f.safePoints.len == 0: f = f.next if f.isNil: return -1 var pc2 = f.safePoints[f.safePoints.high] var nextExceptOrFinally = -1 if c.code[pc2].opcode == opcExcept: nextExceptOrFinally = pc2 + c.code[pc2].regBx - wordExcess inc pc2 while c.code[pc2].opcode == opcExcept: let exceptType = c.types[c.code[pc2].regBx-wordExcess].skipTypes( abstractPtrs) if inheritanceDiff(exceptType, raisedType) <= 0: # mark exception as handled but keep it in B for # the getCurrentException() builtin: c.currentExceptionB = c.currentExceptionA c.currentExceptionA = nil # execute the corresponding handler: return pc2 inc pc2 if nextExceptOrFinally >= 0: pc2 = nextExceptOrFinally if c.code[pc2].opcode == opcFinally: # execute the corresponding handler, but don't quit walking the stack: return pc2 # not the right one: discard f.safePoints.pop proc cleanUpOnReturn(c: PCtx; f: PStackFrame): int = if f.safePoints.isNil: return -1 for s in f.safePoints: var pc = s while c.code[pc].opcode == opcExcept: pc = pc + c.code[pc].regBx - wordExcess if c.code[pc].opcode == opcFinally: return pc return -1 proc opConv*(dest, src: PNode, typ: PType): bool = if typ.kind == tyString: if dest.kind != nkStrLit: myreset(dest) dest.kind = nkStrLit case src.typ.skipTypes(abstractRange).kind of tyEnum: dest.strVal = ordinalValToString(src) of tyInt..tyInt64, tyUInt..tyUInt64: dest.strVal = $src.intVal of tyBool: dest.strVal = if src.intVal == 0: "false" else: "true" of tyFloat..tyFloat128: dest.strVal = $src.floatVal of tyString, tyCString: dest.strVal = src.strVal of tyChar: dest.strVal = $chr(src.intVal) else: internalError("cannot convert to string " & typ.typeToString) else: case skipTypes(typ, abstractRange).kind of tyInt..tyInt64: if dest.kind != nkIntLit: myreset(dest); dest.kind = nkIntLit case skipTypes(src.typ, abstractRange).kind of tyFloat..tyFloat64: dest.intVal = system.toInt(src.floatVal) else: dest.intVal = src.intVal if dest.intVal < firstOrd(typ) or dest.intVal > lastOrd(typ): return true of tyUInt..tyUInt64: if dest.kind != nkIntLit: myreset(dest); dest.kind = nkIntLit case skipTypes(src.typ, abstractRange).kind of tyFloat..tyFloat64: dest.intVal = system.toInt(src.floatVal) else: dest.intVal = src.intVal and ((1 shl typ.size)-1) of tyFloat..tyFloat64: if dest.kind != nkFloatLit: myreset(dest); dest.kind = nkFloatLit case skipTypes(src.typ, abstractRange).kind of tyInt..tyInt64, tyUInt..tyUInt64, tyEnum, tyBool, tyChar: dest.floatVal = toFloat(src.intVal.int) else: dest.floatVal = src.floatVal else: asgnComplex(dest, src) proc compile(c: PCtx, s: PSym): int = result = vmgen.genProc(c, s) #c.echoCode proc regsContents(regs: TNodeSeq) = for i in 0.. codegen should insert a sub assert regs[rb].kind != nkMetaNode asgnComplex(regs[ra], regs[rb].sons[idx.int]) of opcLdStrIdx: decodeBC(nkIntLit) let idx = regs[rc].intVal regs[ra].intVal = regs[rb].strVal[idx.int].ord of opcWrArr: # a[b] = c let rb = instr.regB let rc = instr.regC let idx = regs[rb].intVal asgnComplex(regs[ra].sons[idx.int], regs[rc]) of opcWrArrRef: let rb = instr.regB let rc = instr.regC let idx = regs[rb].intVal asgnRef(regs[ra].sons[idx.int], regs[rc]) of opcLdObj: # a = b.c let rb = instr.regB let rc = instr.regC # XXX this creates a wrong alias #Message(c.debug[pc], warnUser, $regs[rb].len & " " & $rc) asgnComplex(regs[ra], regs[rb].sons[rc]) of opcWrObj: # a.b = c let rb = instr.regB let rc = instr.regC #if regs[ra].isNil or regs[ra].sons.isNil or rb >= len(regs[ra]): # debug regs[ra] # debug regs[rc] # echo "RB ", rb # internalError(c.debug[pc], "argl") asgnComplex(regs[ra].sons[rb], regs[rc]) of opcWrObjRef: let rb = instr.regB let rc = instr.regC asgnRef(regs[ra].sons[rb], regs[rc]) of opcWrStrIdx: decodeBC(nkStrLit) let idx = regs[rb].intVal.int regs[ra].strVal[idx] = chr(regs[rc].intVal) of opcAddr: decodeB(nkRefTy) if regs[ra].len == 0: regs[ra].add regs[rb] else: regs[ra].sons[0] = regs[rb] of opcDeref: # a = b[] let rb = instr.regB if regs[rb].kind == nkNilLit: stackTrace(c, tos, pc, errNilAccess) assert regs[rb].kind == nkRefTy # XXX this is not correct regs[ra] = regs[rb].sons[0] of opcAddInt: decodeBC(nkIntLit) regs[ra].intVal = regs[rb].intVal + regs[rc].intVal of opcAddImmInt: decodeBImm(nkIntLit) regs[ra].intVal = regs[rb].intVal + imm of opcSubInt: decodeBC(nkIntLit) regs[ra].intVal = regs[rb].intVal - regs[rc].intVal of opcSubImmInt: decodeBImm(nkIntLit) regs[ra].intVal = regs[rb].intVal - imm of opcLenSeq: decodeBImm(nkIntLit) #assert regs[rb].kind == nkBracket # also used by mNLen: regs[ra].intVal = regs[rb].skipMeta.len - imm of opcLenStr: decodeBImm(nkIntLit) assert regs[rb].kind == nkStrLit regs[ra].intVal = regs[rb].strVal.len - imm of opcIncl: decodeB(nkCurly) if not inSet(regs[ra], regs[rb]): addSon(regs[ra], copyTree(regs[rb])) of opcInclRange: decodeBC(nkCurly) var r = newNode(nkRange) r.add regs[rb] r.add regs[rc] addSon(regs[ra], r.copyTree) of opcExcl: decodeB(nkCurly) var b = newNodeIT(nkCurly, regs[rb].info, regs[rb].typ) addSon(b, regs[rb]) var r = diffSets(regs[ra], b) discardSons(regs[ra]) for i in countup(0, sonsLen(r) - 1): addSon(regs[ra], r.sons[i]) of opcCard: decodeB(nkIntLit) regs[ra].intVal = nimsets.cardSet(regs[rb]) of opcMulInt: decodeBC(nkIntLit) regs[ra].intVal = regs[rb].intVal * regs[rc].intVal of opcDivInt: decodeBC(nkIntLit) regs[ra].intVal = regs[rb].intVal div regs[rc].intVal of opcModInt: decodeBC(nkIntLit) regs[ra].intVal = regs[rb].intVal mod regs[rc].intVal of opcAddFloat: decodeBC(nkFloatLit) regs[ra].floatVal = regs[rb].floatVal + regs[rc].floatVal of opcSubFloat: decodeBC(nkFloatLit) regs[ra].floatVal = regs[rb].floatVal - regs[rc].floatVal of opcMulFloat: decodeBC(nkFloatLit) regs[ra].floatVal = regs[rb].floatVal * regs[rc].floatVal of opcDivFloat: decodeBC(nkFloatLit) regs[ra].floatVal = regs[rb].floatVal / regs[rc].floatVal of opcShrInt: decodeBC(nkIntLit) regs[ra].intVal = regs[rb].intVal shr regs[rc].intVal of opcShlInt: decodeBC(nkIntLit) regs[ra].intVal = regs[rb].intVal shl regs[rc].intVal of opcBitandInt: decodeBC(nkIntLit) regs[ra].intVal = regs[rb].intVal and regs[rc].intVal of opcBitorInt: decodeBC(nkIntLit) regs[ra].intVal = regs[rb].intVal or regs[rc].intVal of opcBitxorInt: decodeBC(nkIntLit) regs[ra].intVal = regs[rb].intVal xor regs[rc].intVal of opcAddu: decodeBC(nkIntLit) regs[ra].intVal = regs[rb].intVal +% regs[rc].intVal of opcSubu: decodeBC(nkIntLit) regs[ra].intVal = regs[rb].intVal -% regs[rc].intVal of opcMulu: decodeBC(nkIntLit) regs[ra].intVal = regs[rb].intVal *% regs[rc].intVal of opcDivu: decodeBC(nkIntLit) regs[ra].intVal = regs[rb].intVal /% regs[rc].intVal of opcModu: decodeBC(nkIntLit) regs[ra].intVal = regs[rb].intVal %% regs[rc].intVal of opcEqInt: decodeBC(nkIntLit) regs[ra].intVal = ord(regs[rb].intVal == regs[rc].intVal) of opcLeInt: decodeBC(nkIntLit) regs[ra].intVal = ord(regs[rb].intVal <= regs[rc].intVal) of opcLtInt: decodeBC(nkIntLit) regs[ra].intVal = ord(regs[rb].intVal < regs[rc].intVal) of opcEqFloat: decodeBC(nkIntLit) regs[ra].intVal = ord(regs[rb].floatVal == regs[rc].floatVal) of opcLeFloat: decodeBC(nkIntLit) regs[ra].intVal = ord(regs[rb].floatVal <= regs[rc].floatVal) of opcLtFloat: decodeBC(nkIntLit) regs[ra].intVal = ord(regs[rb].floatVal < regs[rc].floatVal) of opcLeu: decodeBC(nkIntLit) regs[ra].intVal = ord(regs[rb].intVal <=% regs[rc].intVal) of opcLtu: decodeBC(nkIntLit) regs[ra].intVal = ord(regs[rb].intVal <% regs[rc].intVal) of opcEqRef: decodeBC(nkIntLit) regs[ra].intVal = ord((regs[rb].kind == nkNilLit and regs[rc].kind == nkNilLit) or regs[rb].sons == regs[rc].sons) of opcEqNimrodNode: decodeBC(nkIntLit) regs[ra].intVal = ord(regs[rb].uast == regs[rc].uast) of opcXor: decodeBC(nkIntLit) regs[ra].intVal = ord(regs[rb].intVal != regs[rc].intVal) of opcNot: decodeB(nkIntLit) assert regs[rb].kind == nkIntLit regs[ra].intVal = 1 - regs[rb].intVal of opcUnaryMinusInt: decodeB(nkIntLit) assert regs[rb].kind == nkIntLit regs[ra].intVal = -regs[rb].intVal of opcUnaryMinusFloat: decodeB(nkFloatLit) assert regs[rb].kind == nkFloatLit regs[ra].floatVal = -regs[rb].floatVal of opcBitnotInt: decodeB(nkIntLit) assert regs[rb].kind == nkIntLit regs[ra].intVal = not regs[rb].intVal of opcEqStr: decodeBC(nkIntLit) regs[ra].intVal = ord(regs[rb].strVal == regs[rc].strVal) of opcLeStr: decodeBC(nkIntLit) regs[ra].intVal = ord(regs[rb].strVal <= regs[rc].strVal) of opcLtStr: decodeBC(nkIntLit) regs[ra].intVal = ord(regs[rb].strVal < regs[rc].strVal) of opcLeSet: decodeBC(nkIntLit) regs[ra].intVal = ord(containsSets(regs[rb], regs[rc])) of opcEqSet: decodeBC(nkIntLit) regs[ra].intVal = ord(equalSets(regs[rb], regs[rc])) of opcLtSet: decodeBC(nkIntLit) let a = regs[rb] let b = regs[rc] regs[ra].intVal = ord(containsSets(a, b) and not equalSets(a, b)) of opcMulSet: decodeBC(nkCurly) move(regs[ra].sons, nimsets.intersectSets(regs[rb], regs[rc]).sons) of opcPlusSet: decodeBC(nkCurly) move(regs[ra].sons, nimsets.unionSets(regs[rb], regs[rc]).sons) of opcMinusSet: decodeBC(nkCurly) move(regs[ra].sons, nimsets.diffSets(regs[rb], regs[rc]).sons) of opcSymDiffSet: decodeBC(nkCurly) move(regs[ra].sons, nimsets.symdiffSets(regs[rb], regs[rc]).sons) of opcConcatStr: decodeBC(nkStrLit) regs[ra].strVal = getstr(regs[rb]) for i in rb+1..rb+rc-1: regs[ra].strVal.add getstr(regs[i]) of opcAddStrCh: decodeB(nkStrLit) regs[ra].strVal.add(regs[rb].intVal.chr) of opcAddStrStr: decodeB(nkStrLit) regs[ra].strVal.add(regs[rb].strVal) of opcAddSeqElem: decodeB(nkBracket) regs[ra].add(copyTree(regs[rb])) of opcEcho: let rb = instr.regB for i in ra..ra+rb-1: #if regs[i].kind != nkStrLit: debug regs[i] write(stdout, regs[i].strVal) writeln(stdout, "") of opcContainsSet: decodeBC(nkIntLit) regs[ra].intVal = ord(inSet(regs[rb], regs[rc])) of opcSubStr: decodeBC(nkStrLit) inc pc assert c.code[pc].opcode == opcSubStr let rd = c.code[pc].regA regs[ra].strVal = substr(regs[rb].strVal, regs[rc].intVal.int, regs[rd].intVal.int) of opcRangeChck: let rb = instr.regB let rc = instr.regC if not (leValueConv(regs[rb], regs[ra]) and leValueConv(regs[ra], regs[rc])): stackTrace(c, tos, pc, errGenerated, msgKindToString(errIllegalConvFromXtoY) % [ "unknown type" , "unknown type"]) of opcIndCall, opcIndCallAsgn: # dest = call regStart, n; where regStart = fn, arg1, ... let rb = instr.regB let rc = instr.regC let isClosure = regs[rb].kind == nkPar let prc = if not isClosure: regs[rb].sym else: regs[rb].sons[0].sym if sfImportc in prc.flags: if allowFFI notin c.features: globalError(c.debug[pc], errGenerated, "VM not allowed to do FFI") # we pass 'tos.slots' instead of 'regs' so that the compiler can keep # 'regs' in a register: when hasFFI: let newValue = callForeignFunction(c.globals.sons[prc.position-1], prc.typ, tos.slots, rb+1, rc-1, c.debug[pc]) if newValue.kind != nkEmpty: assert instr.opcode == opcIndCallAsgn regs[ra] = newValue else: globalError(c.debug[pc], errGenerated, "VM not built with FFI support") else: let newPc = compile(c, prc) #echo "new pc ", newPc, " calling: ", prc.name.s var newFrame = PStackFrame(prc: prc, comesFrom: pc, next: tos) newSeq(newFrame.slots, prc.offset) if not isEmptyType(prc.typ.sons[0]): newFrame.slots[0] = getNullValue(prc.typ.sons[0], prc.info) # pass every parameter by var (the language definition allows this): for i in 1 .. rc-1: newFrame.slots[i] = regs[rb+i] if isClosure: newFrame.slots[rc] = regs[rb].sons[1] # allocate the temporaries: for i in rc+ord(isClosure) .. = 0) of opcSetLenSeq: decodeB(nkBracket) let newLen = regs[rb].getOrdValue.int setLen(regs[ra].sons, newLen) of opcSwap, opcCast, opcReset: internalError(c.debug[pc], "too implement") of opcIsNil: decodeB(nkIntLit) regs[ra].intVal = ord(regs[rb].skipMeta.kind == nkNilLit) of opcNBindSym: # trivial implementation: decodeB(nkMetaNode) setMeta(regs[ra], regs[rb].skipMeta.sons[1]) of opcNChild: decodeBC(nkMetaNode) if regs[rb].kind != nkMetaNode: internalError(c.debug[pc], "no MetaNode") setMeta(regs[ra], regs[rb].uast.sons[regs[rc].intVal.int]) of opcNSetChild: decodeBC(nkMetaNode) regs[ra].uast.sons[regs[rb].intVal.int] = regs[rc].uast of opcNAdd: decodeBC(nkMetaNode) var u = regs[rb].uast u.add(regs[rc].uast) setMeta(regs[ra], u) of opcNAddMultiple: decodeBC(nkMetaNode) let x = regs[rc] var u = regs[rb].uast # XXX can be optimized: for i in 0.. ord(high(TNodeKind)) or k == ord(nkMetaNode): internalError(c.debug[pc], "request to create a NimNode of invalid kind") let cc = regs[rc].skipMeta setMeta(regs[ra], newNodeI(TNodeKind(int(k)), if cc.kind == nkNilLit: c.debug[pc] else: cc.info)) regs[ra].sons[0].flags.incl nfIsRef of opcNCopyNimNode: decodeB(nkMetaNode) setMeta(regs[ra], copyNode(regs[rb])) of opcNCopyNimTree: decodeB(nkMetaNode) setMeta(regs[ra], copyTree(regs[rb])) of opcNDel: decodeBC(nkMetaNode) let bb = regs[rb].intVal.int for i in countup(0, regs[rc].intVal.int-1): delSon(regs[ra].uast, bb) of opcGenSym: decodeBC(nkMetaNode) let k = regs[rb].intVal let name = if regs[rc].strVal.len == 0: ":tmp" else: regs[rc].strVal if k < 0 or k > ord(high(TSymKind)): internalError(c.debug[pc], "request to create symbol of invalid kind") var sym = newSym(k.TSymKind, name.getIdent, c.module, c.debug[pc]) incl(sym.flags, sfGenSym) setMeta(regs[ra], newSymNode(sym)) of opcTypeTrait: # XXX only supports 'name' for now; we can use regC to encode the # type trait operation decodeB(nkStrLit) let typ = regs[rb].sym.typ.skipTypes({tyTypeDesc}) regs[ra].strVal = typ.typeToString(preferExported) inc pc proc fixType(result, n: PNode) {.inline.} = # XXX do it deeply for complex values #if result.typ.isNil: result.typ = n.typ proc execute(c: PCtx, start: int): PNode = var tos = PStackFrame(prc: nil, comesFrom: 0, next: nil) newSeq(tos.slots, c.prc.maxSlots) for i in 0 .. 100: GlobalError(n.info, errTemplateInstantiationTooNested) setupGlobalCtx(module) var c = globalCtx c.callsite = nOrig let start = genProc(c, sym) var tos = PStackFrame(prc: sym, comesFrom: 0, next: nil) let maxSlots = sym.offset newSeq(tos.slots, maxSlots) # setup arguments: var L = n.safeLen if L == 0: L = 1 InternalAssert tos.slots.len >= L # return value: tos.slots[0] = newNodeIT(nkNilLit, n.info, sym.typ.sons[0]) # setup parameters: for i in 1 .. < L: tos.slots[i] = setupMacroParam(n.sons[i]) # temporary storage: for i in L ..