# # # The Nim Compiler # (c) Copyright 2015 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # # included from cgen.nim const RangeExpandLimit = 256 # do not generate ranges # over 'RangeExpandLimit' elements stringCaseThreshold = 8 # above X strings a hash-switch for strings is generated proc getTraverseProc(p: BProc, v: Psym): Rope = if p.config.selectedGC in {gcMarkAndSweep, gcDestructors, gcV2, gcRefc} and optNimV2 notin p.config.globalOptions and containsGarbageCollectedRef(v.loc.t): # we register a specialized marked proc here; this has the advantage # that it works out of the box for thread local storage then :-) result = genTraverseProcForGlobal(p.module, v, v.info) proc registerTraverseProc(p: BProc, v: PSym, traverseProc: Rope) = if sfThread in v.flags: appcg(p.module, p.module.initProc.procSec(cpsInit), "$n\t#nimRegisterThreadLocalMarker($1);$n$n", [traverseProc]) else: appcg(p.module, p.module.initProc.procSec(cpsInit), "$n\t#nimRegisterGlobalMarker($1);$n$n", [traverseProc]) proc isAssignedImmediately(conf: ConfigRef; n: PNode): bool {.inline.} = if n.kind == nkEmpty: return false if isInvalidReturnType(conf, n.typ): # var v = f() # is transformed into: var v; f(addr v) # where 'f' **does not** initialize the result! return false result = true proc inExceptBlockLen(p: BProc): int = for x in p.nestedTryStmts: if x.inExcept: result.inc proc startBlock(p: BProc, start: FormatStr = "{$n", args: varargs[Rope]): int {.discardable.} proc endBlock(p: BProc) proc genVarTuple(p: BProc, n: PNode) = var tup, field: TLoc if n.kind != nkVarTuple: internalError(p.config, n.info, "genVarTuple") var L = sonsLen(n) # if we have a something that's been captured, use the lowering instead: for i in countup(0, L-3): if n[i].kind != nkSym: genStmts(p, lowerTupleUnpacking(p.module.g.graph, n, p.prc)) return # check only the first son var forHcr = treatGlobalDifferentlyForHCR(p.module, n.sons[0].sym) let hcrCond = if forHcr: getTempName(p.module) else: nil var hcrGlobals: seq[tuple[loc: TLoc, tp: Rope]] # determine if the tuple is constructed at top-level scope or inside of a block (if/while/block) let isGlobalInBlock = forHcr and p.blocks.len > 2 # do not close and reopen blocks if this is a 'global' but inside of a block (if/while/block) forHcr = forHcr and not isGlobalInBlock if forHcr: # check with the boolean if the initializing code for the tuple should be ran lineCg(p, cpsStmts, "if ($1)$n", hcrCond) startBlock(p) defer: if forHcr: # end the block where the tuple gets initialized endBlock(p) if forHcr or isGlobalInBlock: # insert the registration of the globals for the different parts of the tuple at the # start of the current scope (after they have been iterated) and init a boolean to # check if any of them is newly introduced and the initializing code has to be ran lineCg(p, cpsLocals, "NIM_BOOL $1 = NIM_FALSE;$n", hcrCond) for curr in hcrGlobals: lineCg(p, cpsLocals, "$1 |= hcrRegisterGlobal($4, \"$2\", sizeof($3), $5, (void**)&$2);$N", hcrCond, curr.loc.r, rdLoc(curr.loc), getModuleDllPath(p.module, n.sons[0].sym), curr.tp) genLineDir(p, n) initLocExpr(p, n.sons[L-1], tup) var t = tup.t.skipTypes(abstractInst) for i in countup(0, L-3): let vn = n.sons[i] let v = vn.sym if sfCompileTime in v.flags: continue var traverseProc: Rope if sfGlobal in v.flags: assignGlobalVar(p, vn) genObjectInit(p, cpsInit, v.typ, v.loc, true) traverseProc = getTraverseProc(p, v) if traverseProc != nil and not p.hcrOn: registerTraverseProc(p, v, traverseProc) else: assignLocalVar(p, vn) initLocalVar(p, v, immediateAsgn=isAssignedImmediately(p.config, n[L-1])) initLoc(field, locExpr, vn, tup.storage) if t.kind == tyTuple: field.r = "$1.Field$2" % [rdLoc(tup), rope(i)] else: if t.n.sons[i].kind != nkSym: internalError(p.config, n.info, "genVarTuple") field.r = "$1.$2" % [rdLoc(tup), mangleRecFieldName(p.module, t.n.sons[i].sym)] putLocIntoDest(p, v.loc, field) if forHcr or isGlobalInBlock: hcrGlobals.add((loc: v.loc, tp: if traverseProc == nil: ~"NULL" else: traverseProc)) proc genDeref(p: BProc, e: PNode, d: var TLoc; enforceDeref=false) proc loadInto(p: BProc, le, ri: PNode, a: var TLoc) {.inline.} = if ri.kind in nkCallKinds and (ri.sons[0].kind != nkSym or ri.sons[0].sym.magic == mNone): genAsgnCall(p, le, ri, a) elif ri.kind in {nkDerefExpr, nkHiddenDeref}: # this is a hacky way to fix #1181 (tmissingderef):: # # var arr1 = cast[ptr array[4, int8]](addr foo)[] # # However, fixing this properly really requires modelling 'array' as # a 'struct' in C to preserve dereferencing semantics completely. Not # worth the effort until version 1.0 is out. genDeref(p, ri, a, enforceDeref=true) else: expr(p, ri, a) proc startBlock(p: BProc, start: FormatStr = "{$n", args: varargs[Rope]): int {.discardable.} = lineCg(p, cpsStmts, start, args) inc(p.labels) result = len(p.blocks) setLen(p.blocks, result + 1) p.blocks[result].id = p.labels p.blocks[result].nestedTryStmts = p.nestedTryStmts.len.int16 p.blocks[result].nestedExceptStmts = p.inExceptBlockLen.int16 proc assignLabel(b: var TBlock): Rope {.inline.} = b.label = "LA" & b.id.rope result = b.label proc blockBody(b: var TBlock): Rope = result = b.sections[cpsLocals] if b.frameLen > 0: result.addf("FR_.len+=$1;$n", [b.frameLen.rope]) result.add(b.sections[cpsInit]) result.add(b.sections[cpsStmts]) proc endBlock(p: BProc, blockEnd: Rope) = let topBlock = p.blocks.len-1 # the block is merged into the parent block add(p.blocks[topBlock-1].sections[cpsStmts], p.blocks[topBlock].blockBody) setLen(p.blocks, topBlock) # this is done after the block is popped so $n is # properly indented when pretty printing is enabled line(p, cpsStmts, blockEnd) proc endBlock(p: BProc) = let topBlock = p.blocks.len - 1 let frameLen = p.blocks[topBlock].frameLen var blockEnd: Rope if frameLen > 0: blockEnd.addf("FR_.len-=$1;$n", [frameLen.rope]) if p.blocks[topBlock].label != nil: blockEnd.addf("} $1: ;$n", [p.blocks[topBlock].label]) else: blockEnd.addf("}$n", []) endBlock(p, blockEnd) proc genSimpleBlock(p: BProc, stmts: PNode) {.inline.} = startBlock(p) genStmts(p, stmts) endBlock(p) proc exprBlock(p: BProc, n: PNode, d: var TLoc) = startBlock(p) expr(p, n, d) endBlock(p) template preserveBreakIdx(body: untyped): untyped = var oldBreakIdx = p.breakIdx body p.breakIdx = oldBreakIdx proc genState(p: BProc, n: PNode) = internalAssert p.config, n.len == 1 let n0 = n[0] if n0.kind == nkIntLit: let idx = n.sons[0].intVal linefmt(p, cpsStmts, "STATE$1: ;$n", idx.rope) elif n0.kind == nkStrLit: linefmt(p, cpsStmts, "$1: ;$n", n0.strVal.rope) proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int) = # Called by return and break stmts. # Deals with issues faced when jumping out of try/except/finally stmts, var stack = newSeq[tuple[n: PNode, inExcept: bool]](0) for i in countup(1, howManyTrys): let tryStmt = p.nestedTryStmts.pop if not p.module.compileToCpp or optNoCppExceptions in p.config.globalOptions: # Pop safe points generated by try if not tryStmt.inExcept and not isDefined(p.config, "nimQuirky"): linefmt(p, cpsStmts, "#popSafePoint();$n") # Pop this try-stmt of the list of nested trys # so we don't infinite recurse on it in the next step. stack.add(tryStmt) # Find finally-stmt for this try-stmt # and generate a copy of its sons var finallyStmt = lastSon(tryStmt.n) if finallyStmt.kind == nkFinally: genStmts(p, finallyStmt.sons[0]) # push old elements again: for i in countdown(howManyTrys-1, 0): p.nestedTryStmts.add(stack[i]) if not p.module.compileToCpp or optNoCppExceptions in p.config.globalOptions: # Pop exceptions that was handled by the # except-blocks we are in for i in countdown(howManyExcepts-1, 0): linefmt(p, cpsStmts, "#popCurrentException();$n") proc genGotoState(p: BProc, n: PNode) = # we resist the temptation to translate it into duff's device as it later # will be translated into computed gotos anyway for GCC at least: # switch (x.state) { # case 0: goto STATE0; # ... var a: TLoc initLocExpr(p, n.sons[0], a) lineF(p, cpsStmts, "switch ($1) {$n", [rdLoc(a)]) p.beforeRetNeeded = true lineF(p, cpsStmts, "case -1:$n", []) blockLeaveActions(p, howManyTrys = p.nestedTryStmts.len, howManyExcepts = p.inExceptBlockLen) lineF(p, cpsStmts, " goto BeforeRet_;$n", []) var statesCounter = lastOrd(p.config, n.sons[0].typ) if n.len >= 2 and n[1].kind == nkIntLit: statesCounter = n[1].intVal let prefix = if n.len == 3 and n[2].kind == nkStrLit: n[2].strVal.rope else: rope"STATE" for i in 0i64 .. statesCounter: lineF(p, cpsStmts, "case $2: goto $1$2;$n", [prefix, rope(i)]) lineF(p, cpsStmts, "}$n", []) proc genBreakState(p: BProc, n: PNode, d: var TLoc) = var a: TLoc initLoc(d, locExpr, n, OnUnknown) if n.sons[0].kind == nkClosure: initLocExpr(p, n.sons[0].sons[1], a) d.r = "(((NI*) $1)[1] < 0)" % [rdLoc(a)] else: initLocExpr(p, n.sons[0], a) # the environment is guaranteed to contain the 'state' field at offset 1: d.r = "((((NI*) $1.ClE_0)[1]) < 0)" % [rdLoc(a)] proc genGotoVar(p: BProc; value: PNode) = if value.kind notin {nkCharLit..nkUInt64Lit}: localError(p.config, value.info, "'goto' target must be a literal value") else: lineF(p, cpsStmts, "goto NIMSTATE_$#;$n", [value.intVal.rope]) proc genSingleVar(p: BProc, a: PNode) = let vn = a.sons[0] let v = vn.sym if sfCompileTime in v.flags: return if sfGoto in v.flags: # translate 'var state {.goto.} = X' into 'goto LX': genGotoVar(p, a.sons[2]) return var targetProc = p var traverseProc: Rope if sfGlobal in v.flags: if v.flags * {sfImportc, sfExportc} == {sfImportc} and a.sons[2].kind == nkEmpty and v.loc.flags * {lfHeader, lfNoDecl} != {}: return if sfPure in v.flags: # v.owner.kind != skModule: targetProc = p.module.preInitProc assignGlobalVar(targetProc, vn) # XXX: be careful here. # Global variables should not be zeromem-ed within loops # (see bug #20). # That's why we are doing the construction inside the preInitProc. # genObjectInit relies on the C runtime's guarantees that # global variables will be initialized to zero. var loc = v.loc # When the native TLS is unavailable, a global thread-local variable needs # one more layer of indirection in order to access the TLS block. # Only do this for complex types that may need a call to `objectInit` if sfThread in v.flags and emulatedThreadVars(p.config) and isComplexValueType(v.typ): initLocExprSingleUse(p.module.preInitProc, vn, loc) genObjectInit(p.module.preInitProc, cpsInit, v.typ, loc, true) # Alternative construction using default constructor (which may zeromem): # if sfImportc notin v.flags: constructLoc(p.module.preInitProc, v.loc) if sfExportc in v.flags and p.module.g.generatedHeader != nil: genVarPrototype(p.module.g.generatedHeader, vn) traverseProc = getTraverseProc(p, v) if traverseProc != nil and not p.hcrOn: registerTraverseProc(p, v, traverseProc) else: let value = a.sons[2] let imm = isAssignedImmediately(p.config, value) if imm and p.module.compileToCpp and p.splitDecls == 0 and not containsHiddenPointer(v.typ): # C++ really doesn't like things like 'Foo f; f = x' as that invokes a # parameterless constructor followed by an assignment operator. So we # generate better code here: 'Foo f = x;' genLineDir(p, a) let decl = localVarDecl(p, vn) var tmp: TLoc if value.kind in nkCallKinds and value[0].kind == nkSym and sfConstructor in value[0].sym.flags: var params: Rope let typ = skipTypes(value.sons[0].typ, abstractInst) assert(typ.kind == tyProc) for i in 1.. 3 and v.owner.kind == skModule: # put it in the locals section - mainly because of loops which # use the var in a call to resetLoc() in the statements section lineCg(targetProc, cpsLocals, "hcrRegisterGlobal($3, \"$1\", sizeof($2), $4, (void**)&$1);$n", v.loc.r, rdLoc(v.loc), getModuleDllPath(p.module, v), traverseProc) # nothing special left to do later on - let's avoid closing and reopening blocks forHcr = false # we close and reopen the global if (nim_hcr_do_init_) blocks in the main Init function # for the module so we can have globals and top-level code be interleaved and still # be able to re-run it but without the top level code - just the init of globals if forHcr: lineCg(targetProc, cpsStmts, "if (hcrRegisterGlobal($3, \"$1\", sizeof($2), $4, (void**)&$1))$N", v.loc.r, rdLoc(v.loc), getModuleDllPath(p.module, v), traverseProc) startBlock(targetProc) defer: if forHcr: endBlock(targetProc) if a.sons[2].kind != nkEmpty: genLineDir(targetProc, a) loadInto(targetProc, a.sons[0], a.sons[2], v.loc) proc genClosureVar(p: BProc, a: PNode) = var immediateAsgn = a.sons[2].kind != nkEmpty var v: TLoc initLocExpr(p, a.sons[0], v) genLineDir(p, a) if immediateAsgn: loadInto(p, a.sons[0], a.sons[2], v) else: constructLoc(p, v) proc genVarStmt(p: BProc, n: PNode) = for it in n.sons: if it.kind == nkCommentStmt: continue if it.kind == nkIdentDefs: # can be a lifted var nowadays ... if it.sons[0].kind == nkSym: genSingleVar(p, it) else: genClosureVar(p, it) else: genVarTuple(p, it) proc genIf(p: BProc, n: PNode, d: var TLoc) = # # { if (!expr1) goto L1; # thenPart } # goto LEnd # L1: # { if (!expr2) goto L2; # thenPart2 } # goto LEnd # L2: # { elsePart } # Lend: var a: TLoc lelse: TLabel if not isEmptyType(n.typ) and d.k == locNone: getTemp(p, n.typ, d) genLineDir(p, n) let lend = getLabel(p) for it in n.sons: # bug #4230: avoid false sharing between branches: if d.k == locTemp and isEmptyType(n.typ): d.k = locNone if it.len == 2: startBlock(p) initLocExprSingleUse(p, it.sons[0], a) lelse = getLabel(p) inc(p.labels) lineF(p, cpsStmts, "if (!$1) goto $2;$n", [rdLoc(a), lelse]) if p.module.compileToCpp: # avoid "jump to label crosses initialization" error: add(p.s(cpsStmts), "{") expr(p, it.sons[1], d) add(p.s(cpsStmts), "}") else: expr(p, it.sons[1], d) endBlock(p) if sonsLen(n) > 1: lineF(p, cpsStmts, "goto $1;$n", [lend]) fixLabel(p, lelse) elif it.len == 1: startBlock(p) expr(p, it.sons[0], d) endBlock(p) else: internalError(p.config, n.info, "genIf()") if sonsLen(n) > 1: fixLabel(p, lend) proc genReturnStmt(p: BProc, t: PNode) = if nfPreventCg in t.flags: return p.beforeRetNeeded = true genLineDir(p, t) if (t.sons[0].kind != nkEmpty): genStmts(p, t.sons[0]) blockLeaveActions(p, howManyTrys = p.nestedTryStmts.len, howManyExcepts = p.inExceptBlockLen) if (p.finallySafePoints.len > 0) and not isDefined(p.config, "nimQuirky"): # If we're in a finally block, and we came here by exception # consume it before we return. var safePoint = p.finallySafePoints[p.finallySafePoints.len-1] linefmt(p, cpsStmts, "if ($1.status != 0) #popCurrentException();$n", safePoint) lineF(p, cpsStmts, "goto BeforeRet_;$n", []) proc genGotoForCase(p: BProc; caseStmt: PNode) = for i in 1 ..< caseStmt.len: startBlock(p) let it = caseStmt.sons[i] for j in 0 .. it.len-2: if it.sons[j].kind == nkRange: localError(p.config, it.info, "range notation not available for computed goto") return let val = getOrdValue(it.sons[j]) lineF(p, cpsStmts, "NIMSTATE_$#:$n", [val.rope]) genStmts(p, it.lastSon) endBlock(p) iterator fieldValuePairs(n: PNode): tuple[memberSym, valueSym: PNode] = assert(n.kind in {nkLetSection, nkVarSection}) for identDefs in n: if identDefs.kind == nkIdentDefs: let valueSym = identDefs[^1] for i in 0 ..< identDefs.len-2: let memberSym = identDefs[i] yield((memberSym: memberSym, valueSym: valueSym)) proc genComputedGoto(p: BProc; n: PNode) = # first pass: Generate array of computed labels: var casePos = -1 var arraySize: int for i in 0 ..< n.len: let it = n.sons[i] if it.kind == nkCaseStmt: if lastSon(it).kind != nkOfBranch: localError(p.config, it.info, "case statement must be exhaustive for computed goto"); return casePos = i if enumHasHoles(it.sons[0].typ): localError(p.config, it.info, "case statement cannot work on enums with holes for computed goto"); return let aSize = lengthOrd(p.config, it.sons[0].typ) if aSize > 10_000: localError(p.config, it.info, "case statement has too many cases for computed goto"); return arraySize = aSize.int if firstOrd(p.config, it.sons[0].typ) != 0: localError(p.config, it.info, "case statement has to start at 0 for computed goto"); return if casePos < 0: localError(p.config, n.info, "no case statement found for computed goto"); return var id = p.labels+1 inc p.labels, arraySize+1 let tmp = "TMP$1_" % [id.rope] var gotoArray = "static void* $#[$#] = {" % [tmp, arraySize.rope] for i in 1..arraySize-1: gotoArray.addf("&&TMP$#_, ", [rope(id+i)]) gotoArray.addf("&&TMP$#_};$n", [rope(id+arraySize)]) line(p, cpsLocals, gotoArray) for j in 0 ..< casePos: genStmts(p, n.sons[j]) let caseStmt = n.sons[casePos] var a: TLoc initLocExpr(p, caseStmt.sons[0], a) # first goto: lineF(p, cpsStmts, "goto *$#[$#];$n", [tmp, a.rdLoc]) for i in 1 ..< caseStmt.len: startBlock(p) let it = caseStmt.sons[i] for j in 0 .. it.len-2: if it.sons[j].kind == nkRange: localError(p.config, it.info, "range notation not available for computed goto") return let val = getOrdValue(it.sons[j]) lineF(p, cpsStmts, "TMP$#_:$n", [intLiteral(val+id+1)]) genStmts(p, it.lastSon) for j in casePos+1 ..< n.sons.len: genStmts(p, n.sons[j]) for j in 0 ..< casePos: # prevent new local declarations # compile declarations as assignments let it = n.sons[j] if it.kind in {nkLetSection, nkVarSection}: let asgn = copyNode(it) asgn.kind = nkAsgn asgn.sons.setLen 2 for sym, value in it.fieldValuePairs: if value.kind != nkEmpty: asgn.sons[0] = sym asgn.sons[1] = value genStmts(p, asgn) else: genStmts(p, it) var a: TLoc initLocExpr(p, caseStmt.sons[0], a) lineF(p, cpsStmts, "goto *$#[$#];$n", [tmp, a.rdLoc]) endBlock(p) for j in casePos+1 ..< n.sons.len: genStmts(p, n.sons[j]) proc genWhileStmt(p: BProc, t: PNode) = # we don't generate labels here as for example GCC would produce # significantly worse code var a: TLoc assert(sonsLen(t) == 2) inc(p.withinLoop) genLineDir(p, t) preserveBreakIdx: var loopBody = t.sons[1] if loopBody.stmtsContainPragma(wComputedGoto) and hasComputedGoto in CC[p.config.cCompiler].props: # for closure support weird loop bodies are generated: if loopBody.len == 2 and loopBody.sons[0].kind == nkEmpty: loopBody = loopBody.sons[1] genComputedGoto(p, loopBody) else: p.breakIdx = startBlock(p, "while (1) {$n") p.blocks[p.breakIdx].isLoop = true initLocExpr(p, t.sons[0], a) if (t.sons[0].kind != nkIntLit) or (t.sons[0].intVal == 0): let label = assignLabel(p.blocks[p.breakIdx]) lineF(p, cpsStmts, "if (!$1) goto $2;$n", [rdLoc(a), label]) genStmts(p, loopBody) if optProfiler in p.options: # invoke at loop body exit: linefmt(p, cpsStmts, "#nimProfile();$n") endBlock(p) dec(p.withinLoop) proc genBlock(p: BProc, n: PNode, d: var TLoc) = # bug #4505: allocate the temp in the outer scope # so that it can escape the generated {}: if not isEmptyType(n.typ) and d.k == locNone: getTemp(p, n.typ, d) preserveBreakIdx: p.breakIdx = startBlock(p) if n.sons[0].kind != nkEmpty: # named block? assert(n.sons[0].kind == nkSym) var sym = n.sons[0].sym sym.loc.k = locOther sym.position = p.breakIdx+1 expr(p, n.sons[1], d) endBlock(p) proc genParForStmt(p: BProc, t: PNode) = assert(sonsLen(t) == 3) inc(p.withinLoop) genLineDir(p, t) preserveBreakIdx: let forLoopVar = t.sons[0].sym var rangeA, rangeB: TLoc assignLocalVar(p, t.sons[0]) #initLoc(forLoopVar.loc, locLocalVar, forLoopVar.typ, onStack) #discard mangleName(forLoopVar) let call = t.sons[1] initLocExpr(p, call.sons[1], rangeA) initLocExpr(p, call.sons[2], rangeB) # $n at the beginning because of #9710 lineF(p, cpsStmts, "$n#pragma omp $4$n" & "for ($1 = $2; $1 <= $3; ++$1)", [forLoopVar.loc.rdLoc, rangeA.rdLoc, rangeB.rdLoc, call.sons[3].getStr.rope]) p.breakIdx = startBlock(p) p.blocks[p.breakIdx].isLoop = true genStmts(p, t.sons[2]) endBlock(p) dec(p.withinLoop) proc genBreakStmt(p: BProc, t: PNode) = var idx = p.breakIdx if t.sons[0].kind != nkEmpty: # named break? assert(t.sons[0].kind == nkSym) var sym = t.sons[0].sym doAssert(sym.loc.k == locOther) idx = sym.position-1 else: # an unnamed 'break' can only break a loop after 'transf' pass: while idx >= 0 and not p.blocks[idx].isLoop: dec idx if idx < 0 or not p.blocks[idx].isLoop: internalError(p.config, t.info, "no loop to break") let label = assignLabel(p.blocks[idx]) blockLeaveActions(p, p.nestedTryStmts.len - p.blocks[idx].nestedTryStmts, p.inExceptBlockLen - p.blocks[idx].nestedExceptStmts) genLineDir(p, t) lineF(p, cpsStmts, "goto $1;$n", [label]) proc genRaiseStmt(p: BProc, t: PNode) = if p.module.compileToCpp: discard cgsym(p.module, "popCurrentExceptionEx") if p.nestedTryStmts.len > 0 and p.nestedTryStmts[^1].inExcept: # if the current try stmt have a finally block, # we must execute it before reraising var finallyBlock = p.nestedTryStmts[^1].n[^1] if finallyBlock.kind == nkFinally: genSimpleBlock(p, finallyBlock[0]) if t[0].kind != nkEmpty: var a: TLoc initLocExprSingleUse(p, t[0], a) var e = rdLoc(a) var typ = skipTypes(t[0].typ, abstractPtrs) genLineDir(p, t) if isImportedException(typ, p.config): lineF(p, cpsStmts, "throw $1;$n", [e]) else: lineCg(p, cpsStmts, "#raiseExceptionEx((#Exception*)$1, $2, $3, $4, $5);$n", [e, makeCString(typ.sym.name.s), makeCString(if p.prc != nil: p.prc.name.s else: p.module.module.name.s), makeCString(toFileName(p.config, t.info)), rope(toLinenumber(t.info))]) else: genLineDir(p, t) # reraise the last exception: if p.module.compileToCpp and optNoCppExceptions notin p.config.globalOptions: line(p, cpsStmts, ~"throw;$n") else: linefmt(p, cpsStmts, "#reraiseException();$n") proc genCaseGenericBranch(p: BProc, b: PNode, e: TLoc, rangeFormat, eqFormat: FormatStr, labl: TLabel) = var x, y: TLoc var length = sonsLen(b) for i in countup(0, length - 2): if b.sons[i].kind == nkRange: initLocExpr(p, b.sons[i].sons[0], x) initLocExpr(p, b.sons[i].sons[1], y) lineCg(p, cpsStmts, rangeFormat, [rdCharLoc(e), rdCharLoc(x), rdCharLoc(y), labl]) else: initLocExpr(p, b.sons[i], x) lineCg(p, cpsStmts, eqFormat, [rdCharLoc(e), rdCharLoc(x), labl]) proc genCaseSecondPass(p: BProc, t: PNode, d: var TLoc, labId, until: int): TLabel = var lend = getLabel(p) for i in 1..until: # bug #4230: avoid false sharing between branches: if d.k == locTemp and isEmptyType(t.typ): d.k = locNone lineF(p, cpsStmts, "LA$1_: ;$n", [rope(labId + i)]) if t.sons[i].kind == nkOfBranch: var length = sonsLen(t.sons[i]) exprBlock(p, t.sons[i].sons[length - 1], d) lineF(p, cpsStmts, "goto $1;$n", [lend]) else: exprBlock(p, t.sons[i].sons[0], d) result = lend proc genIfForCaseUntil(p: BProc, t: PNode, d: var TLoc, rangeFormat, eqFormat: FormatStr, until: int, a: TLoc): TLabel = # generate a C-if statement for a Nim case statement var labId = p.labels for i in 1..until: inc(p.labels) if t.sons[i].kind == nkOfBranch: # else statement genCaseGenericBranch(p, t.sons[i], a, rangeFormat, eqFormat, "LA" & rope(p.labels) & "_") else: lineF(p, cpsStmts, "goto LA$1_;$n", [rope(p.labels)]) if until < t.len-1: inc(p.labels) var gotoTarget = p.labels lineF(p, cpsStmts, "goto LA$1_;$n", [rope(gotoTarget)]) result = genCaseSecondPass(p, t, d, labId, until) lineF(p, cpsStmts, "LA$1_: ;$n", [rope(gotoTarget)]) else: result = genCaseSecondPass(p, t, d, labId, until) proc genCaseGeneric(p: BProc, t: PNode, d: var TLoc, rangeFormat, eqFormat: FormatStr) = var a: TLoc initLocExpr(p, t.sons[0], a) var lend = genIfForCaseUntil(p, t, d, rangeFormat, eqFormat, sonsLen(t)-1, a) fixLabel(
#
#
#            Nim's Runtime Library
#        (c) Copyright 2017 Nim contributors
#
#    See the file "copying.txt", included in this
#    distribution, for details about the copyright.
#

##[
String `interpolation`:idx: / `format`:idx: inspired by
Python's ``f``-strings.

``fmt`` vs. ``&``
=================

You can use either ``fmt`` or the unary ``&`` operator for formatting. The
difference between them is subtle but important.

The ``fmt"{expr}"`` syntax is more aesthetically pleasing, but it hides a small
gotcha. The string is a
`generalized raw string literal <manual.html#lexical-analysis-generalized-raw-string-literals>`_.
This has some surprising effects:

.. code-block:: nim

    import strformat
    let msg = "hello"
    doAssert fmt"{msg}\n" == "hello\\n"

Because the literal is a raw string literal, the ``\n`` is not interpreted as
an escape sequence.

There are multiple ways to get around this, including the use of the ``&``
operator:

.. code-block:: nim

    import strformat
    let msg = "hello"

    doAssert &"{msg}\n" == "hello\n"

    doAssert fmt"{msg}{'\n'}" == "hello\n"
    doAssert fmt("{msg}\n") == "hello\n"
    doAssert "{msg}\n".fmt == "hello\n"

The choice of style is up to you.

Formatting strings
==================

.. code-block:: nim

    import strformat

    doAssert &"""{"abc":>4}""" == " abc"
    doAssert &"""{"abc":<4}""" == "abc "

Formatting floats
=================

.. code-block:: nim

    import strformat

    doAssert fmt"{-12345:08}" == "-0012345"
    doAssert fmt"{-1:3}" == " -1"
    doAssert fmt"{-1:03}" == "-01"
    doAssert fmt"{16:#X}" == "0x10"

    doAssert fmt"{123.456}" == "123.456"
    doAssert fmt"{123.456:>9.3f}" == "  123.456"
    doAssert fmt"{123.456:9.3f}" == "  123.456"
    doAssert fmt"{123.456:9.4f}" == " 123.4560"
    doAssert fmt"{123.456:>9.0f}" == "     123."
    doAssert fmt"{123.456:<9.4f}" == "123.4560 "

    doAssert fmt"{123.456:e}" == "1.234560e+02"
    doAssert fmt"{123.456:>13e}" == " 1.234560e+02"
    doAssert fmt"{123.456:13e}" == " 1.234560e+02"


Implementation details
======================

An expression like ``&"{key} is {value:arg} {{z}}"`` is transformed into:

.. code-block:: nim
  var temp = newStringOfCap(educatedCapGuess)
  format(key, temp)
  format(" is ", temp)
  format(value, arg, temp)
  format(" {z}", temp)
  temp

Parts of the string that are enclosed in the curly braces are interpreted
as Nim code, to escape an ``{`` or ``}`` double it.

``&`` delegates most of the work to an open overloaded set
of ``format`` procs. The required signature for a type ``T`` that supports
formatting is usually ``proc format(x: T; result: var string)`` for efficiency
but can also be ``proc format(x: T): string``. ``add`` and ``$`` procs are
used as the fallback implementation.

This is the concrete lookup algorithm that ``&`` uses:

.. code-block:: nim

  when compiles(format(arg, res)):
    format(arg, res)
  elif compiles(format(arg)):
    res.add format(arg)
  elif compiles(add(res, arg)):
    res.add(arg)
  else:
    res.add($arg)


The subexpression after the colon
(``arg`` in ``&"{key} is {value:arg} {{z}}"``) is an optional argument
passed to ``format``.

If an optional argument is present the following lookup algorithm is used:

.. code-block:: nim

  when compiles(format(arg, option, res)):
    format(arg, option, res)
  else:
    res.add format(arg, option)


For strings and numeric types the optional argument is a so-called
"standard format specifier".


Standard format specifier for strings, integers and floats
==========================================================


The general form of a standard format specifier is::

  [[fill]align][sign][#][0][minimumwidth][.precision][type]

The square brackets ``[]`` indicate an optional element.

The optional align flag can be one of the following:

'<'
    Forces the field to be left-aligned within the available
    space. (This is the default for strings.)

'>'
    Forces the field to be right-aligned within the available space.
    (This is the default for numbers.)

'^'
    Forces the field to be centered within the available space.

Note that unless a minimum field width is defined, the field width
will always be the same size as the data to fill it, so that the alignment
option has no meaning in this case.

The optional 'fill' character defines the character to be used to pad
the field to the minimum width. The fill character, if present, must be
followed by an alignment flag.

The 'sign' option is only valid for numeric types, and can be one of the following:

=================        ====================================================
  Sign                   Meaning
=================        ====================================================
``+``                    Indicates that a sign should be used for both
                         positive as well as negative numbers.
``-``                    Indicates that a sign should be used only for
                         negative numbers (this is the default behavior).
(space)                  Indicates that a leading space should be used on
                         positive numbers.
=================        ====================================================

If the '#' character is present, integers use the 'alternate form' for formatting.
This means that binary, octal, and hexadecimal output will be prefixed
with '0b', '0o', and '0x', respectively.

'width' is a decimal integer defining the minimum field width. If not specified,
then the field width will be determined by the content.

If the width field is preceded by a zero ('0') character, this enables
zero-padding.

The 'precision' is a decimal number indicating how many digits should be displayed
after the decimal point in a floating point conversion. For non-numeric types the
field indicates the maximum field size - in other words, how many characters will
be used from the field content. The precision is ignored for integer conversions.

Finally, the 'type' determines how the data should be presented.

The available integer presentation types are:


=================        ====================================================
  Type                   Result
=================        ====================================================
``b``                    Binary. Outputs the number in base 2.
``d``                    Decimal Integer. Outputs the number in base 10.
``o``                    Octal format. Outputs the number in base 8.
``x``                    Hex format. Outputs the number in base 16, using
                         lower-case letters for the digits above 9.
``X``                    Hex format. Outputs the number in base 16, using
                         uppercase letters for the digits above 9.
(None)                   the same as 'd'
=================        ====================================================


The available floating point presentation types are:

=================        ====================================================
  Type                   Result
=================        ====================================================
``e``                    Exponent notation. Prints the number in scientific
                         notation using the letter 'e' to indicate the
                         exponent.
``E``                    Exponent notation. Same as 'e' except it converts
                         the number to uppercase.
``f``                    Fixed point. Displays the number as a fixed-point
                         number.
``F``                    Fixed point. Same as 'f' except it converts the
                         number to uppercase.
``g``                    General format. This prints the number as a
                         fixed-point number, unless the number is too
                         large, in which case it switches to 'e'
                         exponent notation.
``G``                    General format. Same as 'g' except switches to 'E'
                         if the