diff options
81 files changed, 1314 insertions, 425 deletions
diff --git a/.gitignore b/.gitignore index fc090130b..f2037c1b8 100644 --- a/.gitignore +++ b/.gitignore @@ -58,6 +58,7 @@ dist/ .*/ ~* -# testament cruft +# testament cruft; TODO: generate these in a gitignore'd dir in the first place. testresults/ test.txt +/test.ini diff --git a/changelog.md b/changelog.md index 43b351ef1..045286ef9 100644 --- a/changelog.md +++ b/changelog.md @@ -22,7 +22,8 @@ become an error in the future. - The ``'c`` and ``'C'`` prefix for octal literals is now deprecated to bring the language in line with the standard library (e.g. ``parseOct``). - +- The dot style for import paths (e.g ``import path.to.module`` instead of + ``import path/to/module``) has been deprecated. #### Breaking changes in the standard library @@ -59,6 +60,8 @@ 1-based coordinates on POSIX for correct behaviour; the Windows behaviour was always correct). +- ``lineInfoObj`` now returns absolute path instead of project path. + It's used by ``lineInfo``, ``check``, ``expect``, ``require``, etc. #### Breaking changes in the compiler @@ -193,4 +196,9 @@ - Nintendo Switch was added as a new platform target. See [the compiler user guide](https://nim-lang.org/docs/nimc.html) for more info. +- macros.bindSym now capable to accepts not only literal string or string constant expression. + bindSym enhancement make it also can accepts computed string or ident node inside macros / + compile time functions / static blocks. Only in templates / regular code it retains it's old behavior. + This new feature can be accessed via {.experimental: "dynamicBindSym".} pragma/switch + ### Bugfixes diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 69175fd18..5af0fe4e0 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -1085,7 +1085,7 @@ proc genStrAppend(p: BProc, e: PNode, d: var TLoc) = # appendChar(s, 'z'); # } var - a, dest: TLoc + a, dest, call: TLoc appends, lens: Rope assert(d.k == locNone) var L = 0 @@ -1109,8 +1109,9 @@ proc genStrAppend(p: BProc, e: PNode, d: var TLoc) = linefmt(p, cpsStmts, "#prepareAdd($1, $2$3);$n", addrLoc(p.config, dest), lens, rope(L)) else: - linefmt(p, cpsStmts, "$1 = #resizeString($1, $2$3);$n", - rdLoc(dest), lens, rope(L)) + initLoc(call, locCall, e, OnHeap) + call.r = ropecg(p.module, "#resizeString($1, $2$3)", [rdLoc(dest), lens, rope(L)]) + genAssignment(p, dest, call, {}) add(p.s(cpsStmts), appends) gcUsage(p.config, e) @@ -1119,17 +1120,20 @@ proc genSeqElemAppend(p: BProc, e: PNode, d: var TLoc) = # seq = (typeof seq) incrSeq(&seq->Sup, sizeof(x)); # seq->data[seq->len-1] = x; let seqAppendPattern = if not p.module.compileToCpp: - "$1 = ($2) #incrSeqV3(&($1)->Sup, $3);$n" + "($2) #incrSeqV3(&($1)->Sup, $3)" else: - "$1 = ($2) #incrSeqV3($1, $3);$n" - var a, b, dest, tmpL: TLoc + "($2) #incrSeqV3($1, $3)" + var a, b, dest, tmpL, call: TLoc initLocExpr(p, e.sons[1], a) initLocExpr(p, e.sons[2], b) let seqType = skipTypes(e.sons[1].typ, {tyVar}) - lineCg(p, cpsStmts, seqAppendPattern, [ - rdLoc(a), - getTypeDesc(p.module, e.sons[1].typ), - genTypeInfo(p.module, seqType, e.info)]) + initLoc(call, locCall, e, OnHeap) + call.r = ropecg(p.module, seqAppendPattern, [rdLoc(a), + getTypeDesc(p.module, e.sons[1].typ), + genTypeInfo(p.module, seqType, e.info)]) + # emit the write barrier if required, but we can always move here, so + # use 'genRefAssign' for the seq. + genRefAssign(p, a, call, {}) #if bt != b.t: # echo "YES ", e.info, " new: ", typeToString(bt), " old: ", typeToString(b.t) initLoc(dest, locExpr, e.sons[2], OnHeap) @@ -1536,7 +1540,7 @@ proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc) = if p.config.selectedGc == gcDestructors: genCall(p, e, d) return - var a, b: TLoc + var a, b, call: TLoc assert(d.k == locNone) var x = e.sons[1] if x.kind in {nkAddr, nkHiddenAddr}: x = x[0] @@ -1544,20 +1548,30 @@ proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc) = initLocExpr(p, e.sons[2], b) let t = skipTypes(e.sons[1].typ, {tyVar}) let setLenPattern = if not p.module.compileToCpp: - "$1 = ($3) #setLengthSeqV2(&($1)->Sup, $4, $2);$n" + "($3) #setLengthSeqV2(&($1)->Sup, $4, $2)" else: - "$1 = ($3) #setLengthSeqV2($1, $4, $2);$n" + "($3) #setLengthSeqV2($1, $4, $2)" - lineCg(p, cpsStmts, setLenPattern, [ + initLoc(call, locCall, e, OnHeap) + call.r = ropecg(p.module, setLenPattern, [ rdLoc(a), rdLoc(b), getTypeDesc(p.module, t), genTypeInfo(p.module, t.skipTypes(abstractInst), e.info)]) + genAssignment(p, a, call, {}) gcUsage(p.config, e) proc genSetLengthStr(p: BProc, e: PNode, d: var TLoc) = if p.config.selectedGc == gcDestructors: binaryStmtAddr(p, e, d, "#setLengthStrV2($1, $2);$n") else: - binaryStmt(p, e, d, "$1 = #setLengthStr($1, $2);$n") + var a, b, call: TLoc + if d.k != locNone: internalError(p.config, e.info, "genSetLengthStr") + initLocExpr(p, e.sons[1], a) + initLocExpr(p, e.sons[2], b) + + initLoc(call, locCall, e, OnHeap) + call.r = ropecg(p.module, "#setLengthStr($1, $2)", [ + rdLoc(a), rdLoc(b)]) + genAssignment(p, a, call, {}) gcUsage(p.config, e) proc genSwap(p: BProc, e: PNode, d: var TLoc) = @@ -1878,7 +1892,12 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = if p.config.selectedGC == gcDestructors: binaryStmtAddr(p, e, d, "#nimAddCharV1($1, $2);$n") else: - binaryStmt(p, e, d, "$1 = #addChar($1, $2);$n") + var dest, b, call: TLoc + initLoc(call, locCall, e, OnHeap) + initLocExpr(p, e.sons[1], dest) + initLocExpr(p, e.sons[2], b) + call.r = ropecg(p.module, "#addChar($1, $2)", [rdLoc(dest), rdLoc(b)]) + genAssignment(p, dest, call, {}) of mAppendStrStr: genStrAppend(p, e, d) of mAppendSeqElem: if p.config.selectedGc == gcDestructors: diff --git a/compiler/closureiters.nim b/compiler/closureiters.nim index 6fa856b2f..9e4885b66 100644 --- a/compiler/closureiters.nim +++ b/compiler/closureiters.nim @@ -588,7 +588,7 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode = let branch = n[i] case branch.kind of nkOfBranch: - branch[1] = ctx.convertExprBodyToAsgn(branch[1], tmp) + branch[^1] = ctx.convertExprBodyToAsgn(branch[^1], tmp) of nkElse: branch[0] = ctx.convertExprBodyToAsgn(branch[0], tmp) else: diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index fcc7998df..d853ce969 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -73,3 +73,4 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimNotNil") defineSymbol("nimVmExportFixed") defineSymbol("nimNewRuntime") + defineSymbol("nimIncrSeqV3") diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 75b599ae9..8d233566c 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -259,7 +259,7 @@ proc nodeToHighlightedHtml(d: PDoc; n: PNode; result: var Rope; renderFlags: TRe of tkSpaces, tkInvalid: add(result, literal) of tkCurlyDotLe: - dispA(d.conf, result, "<span>" & # This span is required for the JS to work properly + dispA(d.conf, result, "<span>" & # This span is required for the JS to work properly """<span class="Other">{</span><span class="Other pragmadots">...</span><span class="Other">}</span> </span> <span class="pragmawrap"> @@ -517,12 +517,11 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) = path = path[cwd.len+1 .. ^1].replace('\\', '/') let gitUrl = getConfigVar(d.conf, "git.url") if gitUrl.len > 0: - var commit = getConfigVar(d.conf, "git.commit") - if commit.len == 0: commit = "master" + let commit = getConfigVar(d.conf, "git.commit", "master") + let develBranch = getConfigVar(d.conf, "git.devel", "devel") dispA(d.conf, seeSrcRope, "$1", "", [ropeFormatNamedVars(d.conf, docItemSeeSrc, - ["path", "line", "url", "commit"], [rope path, - rope($n.info.line), rope gitUrl, - rope commit])]) + ["path", "line", "url", "commit", "devel"], [rope path, + rope($n.info.line), rope gitUrl, rope commit, rope develBranch])]) add(d.section[k], ropeFormatNamedVars(d.conf, getConfigVar(d.conf, "doc.item"), ["name", "header", "desc", "itemID", "header_plain", "itemSym", diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index 575e30a79..16b0d614d 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -92,7 +92,7 @@ compiler nintendoSwitchGCC: optSize: " -Os -ffast-math ", compilerExe: "aarch64-none-elf-gcc", cppCompiler: "aarch64-none-elf-g++", - compileTmpl: "-MMD -MP -MF $dfile -c $options $include -o $objfile $file", + compileTmpl: "-w -MMD -MP -MF $dfile -c $options $include -o $objfile $file", buildGui: " -mwindows", buildDll: " -shared", buildLib: "aarch64-none-elf-gcc-ar rcs $libfile $objfiles", @@ -645,7 +645,7 @@ proc compileCFile(conf: ConfigRef; list: CFileList, script: var Rope, cmds: var if optCompileOnly notin conf.globalOptions: add(cmds, compileCmd) let (_, name, _) = splitFile(it.cname) - add(prettyCmds, "CC: " & name) + add(prettyCmds, if hintCC in conf.notes: "CC: " & name else: "") if optGenScript in conf.globalOptions: add(script, compileCmd) add(script, "\n") @@ -659,7 +659,7 @@ proc getLinkCmd(conf: ConfigRef; projectfile, objfiles: string): string = libname = getCurrentDir() / libname else: libname = (libNameTmpl(conf) % splitFile(conf.projectName).name) - result = CC[conf.cCompiler].buildLib % ["libfile", libname, + result = CC[conf.cCompiler].buildLib % ["libfile", quoteShell(libname), "objfiles", objfiles] else: var linkerExe = getConfigVar(conf, conf.cCompiler, ".linkerexe") @@ -668,8 +668,10 @@ proc getLinkCmd(conf: ConfigRef; projectfile, objfiles: string): string = if needsExeExt(conf): linkerExe = addFileExt(linkerExe, "exe") if noAbsolutePaths(conf): result = linkerExe else: result = joinPath(conf.cCompilerpath, linkerExe) - let buildgui = if optGenGuiApp in conf.globalOptions: CC[conf.cCompiler].buildGui - else: "" + let buildgui = if optGenGuiApp in conf.globalOptions and conf.target.targetOS == osWindows: + CC[conf.cCompiler].buildGui + else: + "" var exefile, builddll: string if optGenDynLib in conf.globalOptions: exefile = platform.OS[conf.target.targetOS].dllFrmt % splitFile(projectfile).name @@ -771,7 +773,8 @@ proc callCCompiler*(conf: ConfigRef; projectfile: string) = var prettyCmds: TStringSeq = @[] let prettyCb = proc (idx: int) = when declared(echo): - echo prettyCmds[idx] + let cmd = prettyCmds[idx] + if cmd != "": echo cmd compileCFile(conf, conf.toCompile, script, cmds, prettyCmds) if optCompileOnly notin conf.globalOptions: execCmdsInParallel(conf, cmds, prettyCb) diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index 0a4801150..d8c0461ce 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -222,7 +222,7 @@ proc interestingIterVar(s: PSym): bool {.inline.} = # XXX optimization: Only lift the variable if it lives across # yield/return boundaries! This can potentially speed up # closure iterators quite a bit. - result = s.kind in {skVar, skLet, skTemp, skForVar} and sfGlobal notin s.flags + result = s.kind in {skResult, skVar, skLet, skTemp, skForVar} and sfGlobal notin s.flags template isIterator*(owner: PSym): bool = owner.kind == skIterator and owner.typ.callConv == ccClosure @@ -458,6 +458,10 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) = of nkLambdaKinds, nkIteratorDef, nkFuncDef: if n.typ != nil: detectCapturedVars(n[namePos], owner, c) + of nkReturnStmt: + if n[0].kind in {nkAsgn, nkFastAsgn}: + detectCapturedVars(n[0].sons[1], owner, c) + else: assert n[0].kind == nkEmpty else: for i in 0..<n.len: detectCapturedVars(n[i], owner, c) @@ -687,6 +691,13 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass; if n.len == 2: n.sons[1] = liftCapturedVars(n[1], owner, d, c) if n[1].kind == nkClosure: result = n[1] + of nkReturnStmt: + if n[0].kind in {nkAsgn, nkFastAsgn}: + # we have a `result = result` expression produced by the closure + # transform, let's not touch the LHS in order to make the lifting pass + # correct when `result` is lifted + n[0].sons[1] = liftCapturedVars(n[0].sons[1], owner, d, c) + else: assert n[0].kind == nkEmpty else: if owner.isIterator: if nfLL in n.flags: @@ -757,6 +768,7 @@ proc liftLambdas*(g: ModuleGraph; fn: PSym, body: PNode; tooEarly: var bool): PN if d.somethingToDo: var c = initLiftingPass(fn) result = liftCapturedVars(body, fn, d, c) + # echo renderTree(result, {renderIds}) if c.envvars.getOrDefault(fn.id) != nil: result = newTree(nkStmtList, rawClosureCreation(fn, d, c), result) else: diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim index b8678e6ba..261dcb44e 100644 --- a/compiler/lineinfos.nim +++ b/compiler/lineinfos.nim @@ -37,7 +37,7 @@ type warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2, warnUninit, warnGcMem, warnDestructor, warnLockLevel, warnResultShadowed, warnInconsistentSpacing, warnUser, - hintSuccess, hintSuccessX, + hintSuccess, hintSuccessX, hintCC, hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded, hintConvFromXtoItselfNotNeeded, hintExprAlwaysX, hintQuitCalled, hintProcessing, hintCodeBegin, hintCodeEnd, hintConf, hintPath, @@ -94,6 +94,7 @@ const warnUser: "$1", hintSuccess: "operation successful", hintSuccessX: "operation successful ($# lines compiled; $# sec total; $#; $#)", + hintCC: "CC: \'$1\'", # unused hintLineTooLong: "line too long", hintXDeclaredButNotUsed: "'$1' is declared but not used", hintConvToBaseNotNeeded: "conversion to base object is not needed", @@ -136,7 +137,7 @@ const "Spacing", "User"] HintsToStr* = [ - "Success", "SuccessX", "LineTooLong", + "Success", "SuccessX", "CC", "LineTooLong", "XDeclaredButNotUsed", "ConvToBaseNotNeeded", "ConvFromXtoItselfNotNeeded", "ExprAlwaysX", "QuitCalled", "Processing", "CodeBegin", "CodeEnd", "Conf", "Path", "CondTrue", "Name", "Pattern", "Exec", "Link", "Dependency", diff --git a/compiler/modulepaths.nim b/compiler/modulepaths.nim index 87c7f3541..e5cbf3a2c 100644 --- a/compiler/modulepaths.nim +++ b/compiler/modulepaths.nim @@ -45,13 +45,6 @@ when false: if best.len > 0 and fileExists(res): result = res -const stdlibDirs = [ - "pure", "core", "arch", - "pure/collections", - "pure/concurrency", "impure", - "wrappers", "wrappers/linenoise", - "windows", "posix", "js"] - when false: proc resolveDollar(project, source, pkg, subdir: string; info: TLineInfo): string = template attempt(a) = @@ -120,7 +113,9 @@ proc getModuleName*(conf: ConfigRef; n: PNode): string = case n.kind of nkStrLit, nkRStrLit, nkTripleStrLit: try: - result = pathSubs(conf, n.strVal, toFullPath(conf, n.info).splitFile().dir) + result = + pathSubs(conf, n.strVal, toFullPath(conf, n.info).splitFile().dir) + .replace(" ") except ValueError: localError(conf, n.info, "invalid path: " & n.strVal) result = n.strVal @@ -147,16 +142,9 @@ proc getModuleName*(conf: ConfigRef; n: PNode): string = result = "" else: let modname = getModuleName(conf, n[2]) - if $n1 == "std": - template attempt(a) = - let x = addFileExt(a, "nim") - if fileExists(x): return x - for candidate in stdlibDirs: - attempt(conf.libpath / candidate / modname) - # hacky way to implement 'x / y /../ z': result = getModuleName(conf, n1) - result.add renderTree(n0, {renderNoComments}) + result.add renderTree(n0, {renderNoComments}).replace(" ") result.add modname of nkPrefix: when false: @@ -167,6 +155,7 @@ proc getModuleName*(conf: ConfigRef; n: PNode): string = # hacky way to implement 'x / y /../ z': result = renderTree(n, {renderNoComments}).replace(" ") of nkDotExpr: + localError(conf, n.info, warnDeprecated, "using '.' instead of '/' in import paths") result = renderTree(n, {renderNoComments}).replace(".", "/") of nkImportAs: result = getModuleName(conf, n.sons[0]) diff --git a/compiler/options.nim b/compiler/options.nim index 98c20a9b5..a776961fc 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -116,7 +116,8 @@ type callOperator, parallel, destructor, - notnil + notnil, + dynamicBindSym SymbolFilesOption* = enum disabledSf, writeOnlySf, readOnlySf, v2Sf @@ -406,8 +407,8 @@ proc mainCommandArg*(conf: ConfigRef): string = proc existsConfigVar*(conf: ConfigRef; key: string): bool = result = hasKey(conf.configVars, key) -proc getConfigVar*(conf: ConfigRef; key: string): string = - result = conf.configVars.getOrDefault key +proc getConfigVar*(conf: ConfigRef; key: string, default = ""): string = + result = conf.configVars.getOrDefault(key, default) proc setConfigVar*(conf: ConfigRef; key, val: string) = conf.configVars[key] = val @@ -556,13 +557,28 @@ proc findFile*(conf: ConfigRef; f: string; suppressStdlib = false): string {.pro result = rawFindFile2(conf, f.toLowerAscii) patchModule(conf) +const stdlibDirs = [ + "pure", "core", "arch", + "pure/collections", + "pure/concurrency", "impure", + "wrappers", "wrappers/linenoise", + "windows", "posix", "js"] + proc findModule*(conf: ConfigRef; modulename, currentModule: string): string = # returns path to module const pkgPrefix = "pkg/" - let m = addFileExt(modulename, NimExt) + const stdPrefix = "std/" + var m = addFileExt(modulename, NimExt) if m.startsWith(pkgPrefix): result = findFile(conf, m.substr(pkgPrefix.len), suppressStdlib = true) else: + if m.startsWith(stdPrefix): + let stripped = m.substr(stdPrefix.len) + for candidate in stdlibDirs: + let path = (conf.libpath / candidate / stripped) + if fileExists(path): + m = path + break let currentPath = currentModule.splitFile.dir result = currentPath / m if not existsFile(result): diff --git a/compiler/parser.nim b/compiler/parser.nim index c513fac68..5664a9f67 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -568,6 +568,7 @@ proc parsePar(p: var TParser): PNode = result = newNodeP(nkPar, p) getTok(p) optInd(p, result) + flexComment(p, result) if p.tok.tokType in {tkDiscard, tkInclude, tkIf, tkWhile, tkCase, tkTry, tkDefer, tkFinally, tkExcept, tkFor, tkBlock, tkConst, tkLet, tkWhen, tkVar, diff --git a/compiler/renderer.nim b/compiler/renderer.nim index 83cf288ff..ce27e1cd9 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -627,15 +627,23 @@ proc gstmts(g: var TSrcGen, n: PNode, c: TContext, doIndent=true) = gcoms(g) if doIndent: dedent(g) else: - if rfLongMode in c.flags: indentNL(g) + indentNL(g) gsub(g, n) gcoms(g) + dedent(g) optNL(g) - if rfLongMode in c.flags: dedent(g) + + +proc gcond(g: var TSrcGen, n: PNode) = + if n.kind == nkStmtListExpr: + put(g, tkParLe, "(") + gsub(g, n) + if n.kind == nkStmtListExpr: + put(g, tkParRi, ")") proc gif(g: var TSrcGen, n: PNode) = var c: TContext - gsub(g, n.sons[0].sons[0]) + gcond(g, n.sons[0].sons[0]) initContext(c) putWithSpace(g, tkColon, ":") if longMode(g, n) or (lsub(g, n.sons[0].sons[1]) + g.lineLen > MaxLineLen): @@ -650,7 +658,7 @@ proc gif(g: var TSrcGen, n: PNode) = proc gwhile(g: var TSrcGen, n: PNode) = var c: TContext putWithSpace(g, tkWhile, "while") - gsub(g, n.sons[0]) + gcond(g, n.sons[0]) putWithSpace(g, tkColon, ":") initContext(c) if longMode(g, n) or (lsub(g, n.sons[1]) + g.lineLen > MaxLineLen): @@ -714,7 +722,7 @@ proc gcase(g: var TSrcGen, n: PNode) = var last = if n.sons[length-1].kind == nkElse: -2 else: -1 if longMode(g, n, 0, last): incl(c.flags, rfLongMode) putWithSpace(g, tkCase, "case") - gsub(g, n.sons[0]) + gcond(g, n.sons[0]) gcoms(g) optNL(g) gsons(g, n, c, 1, last) @@ -785,10 +793,7 @@ proc gblock(g: var TSrcGen, n: PNode) = if longMode(g, n) or (lsub(g, n.sons[1]) + g.lineLen > MaxLineLen): incl(c.flags, rfLongMode) gcoms(g) - # XXX I don't get why this is needed here! gstmts should already handle this! - indentNL(g) gstmts(g, n.sons[1], c) - dedent(g) proc gstaticStmt(g: var TSrcGen, n: PNode) = var c: TContext @@ -1104,13 +1109,13 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = put(g, tkAccent, "`") of nkIfExpr: putWithSpace(g, tkIf, "if") - if n.len > 0: gsub(g, n.sons[0], 0) + if n.len > 0: gcond(g, n.sons[0].sons[0]) putWithSpace(g, tkColon, ":") if n.len > 0: gsub(g, n.sons[0], 1) gsons(g, n, emptyContext, 1) of nkElifExpr: putWithSpace(g, tkElif, " elif") - gsub(g, n, 0) + gcond(g, n[0]) putWithSpace(g, tkColon, ":") gsub(g, n, 1) of nkElseExpr: diff --git a/compiler/sem.nim b/compiler/sem.nim index 0d484d276..2c3fab5a5 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -608,16 +608,18 @@ proc myProcess(context: PPassContext, n: PNode): PNode = rod.storeNode(c.graph, c.module, result) proc testExamples(c: PContext) = + let outputDir = c.config.getNimcacheDir / "runnableExamples" + createDir(outputDir) let inp = toFullPath(c.config, c.module.info) - let outp = inp.changeFileExt"" & "_examples.nim" + let outp = outputDir / extractFilename(inp.changeFileExt"" & "_examples.nim") let nimcache = outp.changeFileExt"" & "_nimcache" - renderModule(c.runnableExamples, inp, outp) + renderModule(c.runnableExamples, inp, outp, conf = c.config) let backend = if isDefined(c.config, "js"): "js" elif isDefined(c.config, "cpp"): "cpp" elif isDefined(c.config, "objc"): "objc" else: "c" if os.execShellCmd(os.getAppFilename() & " " & backend & " --nimcache:" & nimcache & " -r " & outp) != 0: - quit "[Examples] failed" + quit "[Examples] failed: see " & outp else: removeFile(outp) removeFile(outp.changeFileExt(ExeExt)) diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 67fe99232..aa5394a71 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -505,7 +505,15 @@ proc explicitGenericSym(c: PContext, n: PNode, s: PSym): PNode = for i in 1..sonsLen(n)-1: let formal = s.ast.sons[genericParamsPos].sons[i-1].typ - let arg = n[i].typ + var arg = n[i].typ + # try transforming the argument into a static one before feeding it into + # typeRel + if formal.kind == tyStatic and arg.kind != tyStatic: + let evaluated = c.semTryConstExpr(c, n[i]) + if evaluated != nil: + arg = newTypeS(tyStatic, c) + arg.sons = @[evaluated.typ] + arg.n = evaluated let tm = typeRel(m, formal, arg) if tm in {isNone, isConvertible}: return nil var newInst = generateInstance(c, s, m.bindings, n.info) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 3a72d1f5a..c9f9eb33f 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -2031,9 +2031,10 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = c.runnableExamples = newTree(nkStmtList, newTree(nkImportStmt, newStrNode(nkStrLit, expandFilename(inp)))) let imports = newTree(nkStmtList) - extractImports(n.lastSon, imports) + var saved_lastSon = copyTree n.lastSon + extractImports(saved_lastSon, imports) for imp in imports: c.runnableExamples.add imp - c.runnableExamples.add newTree(nkBlockStmt, c.graph.emptyNode, copyTree n.lastSon) + c.runnableExamples.add newTree(nkBlockStmt, c.graph.emptyNode, copyTree saved_lastSon) result = setMs(n, s) else: result = c.graph.emptyNode @@ -2126,7 +2127,7 @@ proc semSetConstr(c: PContext, n: PNode): PNode = n.sons[i] = semExprWithType(c, n.sons[i]) if typ == nil: typ = skipTypes(n.sons[i].typ, {tyGenericInst, tyVar, tyLent, tyOrdinal, tyAlias, tySink}) - if not isOrdinalType(typ): + if not isOrdinalType(typ, allowEnumWithHoles=true): localError(c.config, n.info, errOrdinalTypeExpected) typ = makeRangeType(c, 0, MaxSetElements-1, n.info) elif lengthOrd(c.config, typ) > MaxSetElements: diff --git a/compiler/semfold.nim b/compiler/semfold.nim index 2f495bc7f..d2abfac13 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -609,7 +609,7 @@ proc getConstExpr(m: PSym, n: PNode; g: ModuleGraph): PNode = if computeSize(g.config, a.typ) < 0: localError(g.config, a.info, "cannot evaluate 'sizeof' because its type is not defined completely") result = nil - elif skipTypes(a.typ, typedescInst+{tyRange}).kind in + elif skipTypes(a.typ, typedescInst+{tyRange, tyArray}).kind in IntegralTypes+NilableTypes+{tySet}: #{tyArray,tyObject,tyTuple}: result = newIntNodeT(getSize(g.config, a.typ), n, g) diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index b5875c67a..8bfa5545e 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -207,6 +207,67 @@ proc semBindSym(c: PContext, n: PNode): PNode = else: errorUndeclaredIdentifier(c, n.sons[1].info, sl.strVal) +proc opBindSym(c: PContext, scope: PScope, n: PNode, isMixin: int, info: PNode): PNode = + if n.kind notin {nkStrLit, nkRStrLit, nkTripleStrLit, nkIdent}: + localError(c.config, info.info, errStringOrIdentNodeExpected) + return errorNode(c, n) + + if isMixin < 0 or isMixin > high(TSymChoiceRule).int: + localError(c.config, info.info, errConstExprExpected) + return errorNode(c, n) + + let id = if n.kind == nkIdent: n + else: newIdentNode(getIdent(c.cache, n.strVal), info.info) + + let tmpScope = c.currentScope + c.currentScope = scope + let s = qualifiedLookUp(c, id, {checkUndeclared}) + if s != nil: + # we need to mark all symbols: + result = symChoice(c, id, s, TSymChoiceRule(isMixin)) + else: + errorUndeclaredIdentifier(c, info.info, if n.kind == nkIdent: n.ident.s + else: n.strVal) + c.currentScope = tmpScope + +proc semDynamicBindSym(c: PContext, n: PNode): PNode = + # inside regular code, bindSym resolves to the sym-choice + # nodes (see tinspectsymbol) + if not (c.inStaticContext > 0 or getCurrOwner(c).isCompileTimeProc): + return semBindSym(c, n) + + if c.graph.vm.isNil: + setupGlobalCtx(c.module, c.graph) + + let + vm = PCtx c.graph.vm + # cache the current scope to + # prevent it lost into oblivion + scope = c.currentScope + + # cannot use this + # vm.config.features.incl dynamicBindSym + + proc bindSymWrapper(a: VmArgs) = + # capture PContext and currentScope + # param description: + # 0. ident, a string literal / computed string / or ident node + # 1. bindSym rule + # 2. info node + a.setResult opBindSym(c, scope, a.getNode(0), a.getInt(1).int, a.getNode(2)) + + let + # altough we use VM callback here, it is not + # executed like 'normal' VM callback + idx = vm.registerCallback("bindSymImpl", bindSymWrapper) + # dummy node to carry idx information to VM + idxNode = newIntTypeNode(nkIntLit, idx, c.graph.getSysType(TLineInfo(), tyInt)) + + result = copyNode(n) + for x in n: result.add x + result.add n # info node + result.add idxNode + proc semShallowCopy(c: PContext, n: PNode, flags: TExprFlags): PNode proc semOf(c: PContext, n: PNode): PNode = @@ -270,7 +331,11 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode, of mOf: result = semOf(c, n) of mHigh, mLow: result = semLowHigh(c, n, n[0].sym.magic) of mShallowCopy: result = semShallowCopy(c, n, flags) - of mNBindSym: result = semBindSym(c, n) + of mNBindSym: + if dynamicBindSym notin c.features: + result = semBindSym(c, n) + else: + result = semDynamicBindSym(c, n) of mProcCall: result = n result.typ = n[1].typ diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 29ed1b870..28dd15209 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -11,6 +11,7 @@ # included from sem.nim const + errStringOrIdentNodeExpected = "string or ident node expected" errStringLiteralExpected = "string literal expected" errIntLiteralExpected = "integer literal expected" errWrongNumberOfVariables = "wrong number of variables" @@ -136,7 +137,7 @@ proc semSet(c: PContext, n: PNode, prev: PType): PType = addSonSkipIntLit(result, base) if base.kind in {tyGenericInst, tyAlias, tySink}: base = lastSon(base) if base.kind != tyGenericParam: - if not isOrdinalType(base): + if not isOrdinalType(base, allowEnumWithHoles = true): localError(c.config, n.info, errOrdinalTypeExpected) elif lengthOrd(c.config, base) > MaxSetElements: localError(c.config, n.info, errSetTooBig) @@ -1508,26 +1509,20 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = of mSet: result = semSet(c, n, prev) of mOrdinal: result = semOrdinal(c, n, prev) of mSeq: - let s = c.graph.sysTypes[tySequence] - assert s != nil - assert prev == nil - result = copyType(s, s.owner, keepId=false) - # XXX figure out why this has children already... - result.sons.setLen 0 - result.n = nil if c.config.selectedGc == gcDestructors: - result.flags = {tfHasAsgn} + let s = c.graph.sysTypes[tySequence] + assert s != nil + assert prev == nil + result = copyType(s, s.owner, keepId=false) + # XXX figure out why this has children already... + result.sons.setLen 0 + result.n = nil + if c.config.selectedGc == gcDestructors: + result.flags = {tfHasAsgn} + else: + result.flags = {} + semContainerArg(c, n, "seq", result) else: - result.flags = {} - semContainerArg(c, n, "seq", result) - when false: - debugT = true - echo "Start!" - #debug result - assert(not containsGenericType(result)) - debugT = false - echo "End!" - when false: result = semContainer(c, n, tySequence, "seq", prev) if c.config.selectedGc == gcDestructors: incl result.flags, tfHasAsgn @@ -1603,8 +1598,14 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = result = prev of nkSym: let s = getGenSym(c, n.sym) - if s.kind == skType and s.typ != nil: - var t = s.typ + if s.kind == skType and s.typ != nil or + s.kind == skParam and s.typ.kind == tyTypeDesc: + var t = + if s.kind == skType: + s.typ + else: + internalAssert c.config, s.typ.base.kind != tyNone and prev == nil + s.typ.base let alias = maybeAliasType(c, t, prev) if alias != nil: result = alias diff --git a/compiler/types.nim b/compiler/types.nim index 60a596a9a..514f5cee5 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -498,9 +498,15 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = add(result, typeToString(t.sons[i])) result.add "]" of tyAnd: - result = typeToString(t.sons[0]) & " and " & typeToString(t.sons[1]) + for i, son in t.sons: + result.add(typeToString(son)) + if i < t.sons.high: + result.add(" and ") of tyOr: - result = typeToString(t.sons[0]) & " or " & typeToString(t.sons[1]) + for i, son in t.sons: + result.add(typeToString(son)) + if i < t.sons.high: + result.add(" or ") of tyNot: result = "not " & typeToString(t.sons[0]) of tyExpr: diff --git a/compiler/vm.nim b/compiler/vm.nim index 373a64e39..a6ec4788b 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -616,19 +616,12 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = let rc = instr.regC case regs[ra].kind of rkNodeAddr: - # XXX: Workaround for vmgen bug: let n = regs[rc].regToNode - if (nfIsRef in regs[ra].nodeAddr[].flags or - regs[ra].nodeAddr[].kind == nkNilLit) and nfIsRef notin n.flags: - if regs[ra].nodeAddr[].kind == nkNilLit: - stackTrace(c, tos, pc, errNilAccess) - regs[ra].nodeAddr[][] = n[] - regs[ra].nodeAddr[].flags.incl nfIsRef # `var object` parameters are sent as rkNodeAddr. When they are mutated # vmgen generates opcWrDeref, which means that we must dereference # twice. # TODO: This should likely be handled differently in vmgen. - elif (nfIsRef notin regs[ra].nodeAddr[].flags and + if (nfIsRef notin regs[ra].nodeAddr[].flags and nfIsRef notin n.flags): regs[ra].nodeAddr[][] = n[] else: @@ -1229,9 +1222,25 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = node.typ.callConv == ccClosure and node.sons[0].kind == nkNilLit and node.sons[1].kind == nkNilLit)) of opcNBindSym: + # cannot use this simple check + # if dynamicBindSym notin c.config.features: + + # bindSym with static input decodeBx(rkNode) regs[ra].node = copyTree(c.constants.sons[rbx]) regs[ra].node.flags.incl nfIsRef + of opcNDynBindSym: + # experimental bindSym + let + rb = instr.regB + rc = instr.regC + idx = int(regs[rb+rc-1].intVal) + callback = c.callbacks[idx].value + args = VmArgs(ra: ra, rb: rb, rc: rc, slots: cast[pointer](regs), + currentException: c.currentExceptionB, + currentLineInfo: c.debug[pc]) + callback(args) + regs[ra].node.flags.incl nfIsRef of opcNChild: decodeBC(rkNode) let idx = regs[rc].intVal.int @@ -1416,7 +1425,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of opcNGetFile: decodeB(rkNode) let n = regs[rb].node - regs[ra].node = newStrNode(nkStrLit, toFilename(c.config, n.info)) + regs[ra].node = newStrNode(nkStrLit, toFullPath(c.config, n.info)) regs[ra].node.info = n.info regs[ra].node.typ = n.typ of opcNGetLine: @@ -1780,7 +1789,7 @@ proc getGlobalValue*(c: PCtx; s: PSym): PNode = include vmops -proc setupGlobalCtx(module: PSym; graph: ModuleGraph) = +proc setupGlobalCtx*(module: PSym; graph: ModuleGraph) = if graph.vm.isNil: graph.vm = newCtx(module, graph.cache, graph) registerAdditionalOps(PCtx graph.vm) diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim index f7466b392..866b79568 100644 --- a/compiler/vmdef.nim +++ b/compiler/vmdef.nim @@ -136,7 +136,7 @@ type opcLdGlobalAddr, # dest = addr(globals[Bx]) opcLdImmInt, # dest = immediate value - opcNBindSym, + opcNBindSym, opcNDynBindSym, opcSetType, # dest.typ = types[Bx] opcTypeTrait, opcMarshalLoad, opcMarshalStore, @@ -229,7 +229,8 @@ proc refresh*(c: PCtx, module: PSym) = c.prc = PProc(blocks: @[]) c.loopIterations = MaxLoopIterations -proc registerCallback*(c: PCtx; name: string; callback: VmCallback) = +proc registerCallback*(c: PCtx; name: string; callback: VmCallback): int {.discardable.} = + result = c.callbacks.len c.callbacks.add((name, callback)) const diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index d2243376c..3b5ea4beb 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -37,7 +37,9 @@ when hasFFI: import evalffi type - TGenFlag = enum gfAddrOf, gfFieldAccess + TGenFlag = enum + gfNode # Affects how variables are loaded - always loads as rkNode + gfNodeAddr # Affects how variables are loaded - always loads as rkNodeAddr TGenFlags = set[TGenFlag] proc debugInfo(c: PCtx; info: TLineInfo): string = @@ -563,7 +565,7 @@ proc genIndex(c: PCtx; n: PNode; arr: PType): TRegister = proc genAsgnPatch(c: PCtx; le: PNode, value: TRegister) = case le.kind of nkBracketExpr: - let dest = c.genx(le.sons[0], {gfAddrOf, gfFieldAccess}) + let dest = c.genx(le.sons[0], {gfNode}) let idx = c.genIndex(le.sons[1], le.sons[0].typ) c.gABC(le, opcWrArr, dest, idx, value) c.freeTemp(dest) @@ -571,17 +573,17 @@ proc genAsgnPatch(c: PCtx; le: PNode, value: TRegister) = of nkDotExpr, nkCheckedFieldExpr: # XXX field checks here let left = if le.kind == nkDotExpr: le else: le.sons[0] - let dest = c.genx(left.sons[0], {gfAddrOf, gfFieldAccess}) + let dest = c.genx(left.sons[0], {gfNode}) let idx = genField(c, left.sons[1]) c.gABC(left, opcWrObj, dest, idx, value) c.freeTemp(dest) of nkDerefExpr, nkHiddenDeref: - let dest = c.genx(le.sons[0], {gfAddrOf}) + let dest = c.genx(le.sons[0], {gfNode}) c.gABC(le, opcWrDeref, dest, 0, value) c.freeTemp(dest) of nkSym: if le.sym.isGlobal: - let dest = c.genx(le, {gfAddrOf}) + let dest = c.genx(le, {gfNodeAddr}) c.gABC(le, opcWrDeref, dest, 0, value) c.freeTemp(dest) else: @@ -815,6 +817,43 @@ proc genVoidABC(c: PCtx, n: PNode, dest: TDest, opcode: TOpcode) = c.freeTemp(tmp2) c.freeTemp(tmp3) +proc genBindSym(c: PCtx; n: PNode; dest: var TDest) = + # nah, cannot use c.config.features because sempass context + # can have local experimental switch + # if dynamicBindSym notin c.config.features: + if n.len == 2: # hmm, reliable? + # bindSym with static input + if n[1].kind in {nkClosedSymChoice, nkOpenSymChoice, nkSym}: + let idx = c.genLiteral(n[1]) + if dest < 0: dest = c.getTemp(n.typ) + c.gABx(n, opcNBindSym, dest, idx) + else: + localError(c.config, n.info, "invalid bindSym usage") + else: + # experimental bindSym + if dest < 0: dest = c.getTemp(n.typ) + let x = c.getTempRange(n.len, slotTempUnknown) + + # callee symbol + var tmp0 = TDest(x) + c.genLit(n.sons[0], tmp0) + + # original parameters + for i in 1..<n.len-2: + var r = TRegister(x+i) + c.gen(n.sons[i], r) + + # info node + var tmp1 = TDest(x+n.len-2) + c.genLit(n.sons[^2], tmp1) + + # payload idx + var tmp2 = TDest(x+n.len-1) + c.genLit(n.sons[^1], tmp2) + + c.gABC(n, opcNDynBindSym, dest, x, n.len) + c.freeTempRange(x, n.len) + proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = case m of mAnd: c.genAndOr(n, opcFJmp, dest) @@ -1135,13 +1174,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = of mNNewNimNode: genBinaryABC(c, n, dest, opcNNewNimNode) of mNCopyNimNode: genUnaryABC(c, n, dest, opcNCopyNimNode) of mNCopyNimTree: genUnaryABC(c, n, dest, opcNCopyNimTree) - of mNBindSym: - if n[1].kind in {nkClosedSymChoice, nkOpenSymChoice, nkSym}: - let idx = c.genLiteral(n[1]) - if dest < 0: dest = c.getTemp(n.typ) - c.gABx(n, opcNBindSym, dest, idx) - else: - localError(c.config, n.info, "invalid bindSym usage") + of mNBindSym: genBindSym(c, n, dest) of mStrToIdent: genUnaryABC(c, n, dest, opcStrToIdent) of mEqIdent: genBinaryABC(c, n, dest, opcEqIdent) of mEqNimrodNode: genBinaryABC(c, n, dest, opcEqNimrodNode) @@ -1252,41 +1285,21 @@ proc canElimAddr(n: PNode): PNode = # addr ( deref ( x )) --> x result = n.sons[0].sons[0] -proc genAddrDeref(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode; - flags: TGenFlags) = - # a nop for certain types - let isAddr = opc in {opcAddrNode, opcAddrReg} - if isAddr and (let m = canElimAddr(n); m != nil): +proc genAddr(c: PCtx, n: PNode, dest: var TDest, flags: TGenFlags) = + if (let m = canElimAddr(n); m != nil): gen(c, m, dest, flags) return - let af = if n[0].kind in {nkBracketExpr, nkDotExpr, nkCheckedFieldExpr}: {gfAddrOf, gfFieldAccess} - else: {gfAddrOf} - let newflags = if isAddr: flags+af else: flags - # consider: - # proc foo(f: var ref int) = - # f = new(int) - # proc blah() = - # var x: ref int - # foo x - # - # The type of 'f' is 'var ref int' and of 'x' is 'ref int'. Hence for - # nkAddr we must not use 'unneededIndirection', but for deref we use it. - if not isAddr and unneededIndirection(n.sons[0]): - gen(c, n.sons[0], dest, newflags) - if gfAddrOf notin flags and fitsRegister(n.typ): - c.gABC(n, opcNodeToReg, dest, dest) - elif isAddr and isGlobal(n.sons[0]): + let af = if n[0].kind in {nkBracketExpr, nkDotExpr, nkCheckedFieldExpr}: {gfNode} + else: {gfNodeAddr} + let newflags = flags-{gfNode, gfNodeAddr}+af + + if isGlobal(n.sons[0]): gen(c, n.sons[0], dest, flags+af) else: let tmp = c.genx(n.sons[0], newflags) if dest < 0: dest = c.getTemp(n.typ) - if not isAddr: - gABC(c, n, opc, dest, tmp) - assert n.typ != nil - if gfAddrOf notin flags and fitsRegister(n.typ): - c.gABC(n, opcNodeToReg, dest, dest) - elif c.prc.slots[tmp].kind >= slotTempUnknown: + if c.prc.slots[tmp].kind >= slotTempUnknown: gABC(c, n, opcAddrNode, dest, tmp) # hack ahead; in order to fix bug #1781 we mark the temporary as # permanent, so that it's not used for anything else: @@ -1297,6 +1310,19 @@ proc genAddrDeref(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode; gABC(c, n, opcAddrReg, dest, tmp) c.freeTemp(tmp) +proc genDeref(c: PCtx, n: PNode, dest: var TDest, flags: TGenFlags) = + if unneededIndirection(n.sons[0]): + gen(c, n.sons[0], dest, flags) + if {gfNodeAddr, gfNode} * flags == {} and fitsRegister(n.typ): + c.gABC(n, opcNodeToReg, dest, dest) + else: + let tmp = c.genx(n.sons[0], flags) + if dest < 0: dest = c.getTemp(n.typ) + gABC(c, n, opcLdDeref, dest, tmp) + assert n.typ != nil + if {gfNodeAddr, gfNode} * flags == {} and fitsRegister(n.typ): + c.gABC(n, opcNodeToReg, dest, dest) + proc whichAsgnOpc(n: PNode): TOpcode = case n.typ.skipTypes(abstractRange-{tyTypeDesc}).kind of tyBool, tyChar, tyEnum, tyOrdinal, tyInt..tyInt64, tyUInt..tyUInt64: @@ -1382,7 +1408,7 @@ proc preventFalseAlias(c: PCtx; n: PNode; opc: TOpcode; proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) = case le.kind of nkBracketExpr: - let dest = c.genx(le.sons[0], {gfAddrOf, gfFieldAccess}) + let dest = c.genx(le.sons[0], {gfNode}) let idx = c.genIndex(le.sons[1], le.sons[0].typ) let tmp = c.genx(ri) if le.sons[0].typ.skipTypes(abstractVarRange-{tyTypeDesc}).kind in { @@ -1394,13 +1420,13 @@ proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) = of nkDotExpr, nkCheckedFieldExpr: # XXX field checks here let left = if le.kind == nkDotExpr: le else: le.sons[0] - let dest = c.genx(left.sons[0], {gfAddrOf, gfFieldAccess}) + let dest = c.genx(left.sons[0], {gfNode}) let idx = genField(c, left.sons[1]) let tmp = c.genx(ri) c.preventFalseAlias(left, opcWrObj, dest, idx, tmp) c.freeTemp(tmp) of nkDerefExpr, nkHiddenDeref: - let dest = c.genx(le.sons[0], {gfAddrOf}) + let dest = c.genx(le.sons[0], {gfNode}) let tmp = c.genx(ri) c.preventFalseAlias(le, opcWrDeref, dest, 0, tmp) c.freeTemp(tmp) @@ -1409,7 +1435,7 @@ proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) = checkCanEval(c, le) if s.isGlobal: withTemp(tmp, le.typ): - c.gen(le, tmp, {gfAddrOf}) + c.gen(le, tmp, {gfNodeAddr}) let val = c.genx(ri) c.preventFalseAlias(le, opcWrDeref, tmp, 0, val) c.freeTemp(val) @@ -1427,7 +1453,7 @@ proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) = else: gen(c, ri, dest) else: - let dest = c.genx(le, {gfAddrOf}) + let dest = c.genx(le, {gfNodeAddr}) genAsgn(c, dest, ri, requiresCopy) proc genTypeLit(c: PCtx; t: PType; dest: var TDest) = @@ -1463,6 +1489,8 @@ proc genGlobalInit(c: PCtx; n: PNode; s: PSym) = c.freeTemp(tmp) proc genRdVar(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) = + # gfNodeAddr and gfNode are mutually exclusive + assert card(flags * {gfNodeAddr, gfNode}) < 2 let s = n.sym if s.isGlobal: if sfCompileTime in s.flags or c.mode == emRepl: @@ -1474,13 +1502,13 @@ proc genRdVar(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) = else: genGlobalInit(c, n, s) if dest < 0: dest = c.getTemp(n.typ) assert s.typ != nil - if gfAddrOf notin flags and fitsRegister(s.typ): + if gfNodeAddr in flags: + c.gABx(n, opcLdGlobalAddr, dest, s.position) + elif fitsRegister(s.typ) and gfNode notin flags: var cc = c.getTemp(n.typ) c.gABx(n, opcLdGlobal, cc, s.position) c.gABC(n, opcNodeToReg, dest, cc) c.freeTemp(cc) - elif {gfAddrOf, gfFieldAccess} * flags == {gfAddrOf}: - c.gABx(n, opcLdGlobalAddr, dest, s.position) else: c.gABx(n, opcLdGlobal, dest, s.position) else: @@ -1498,7 +1526,8 @@ proc genRdVar(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) = cannotEval(c, n) template needsRegLoad(): untyped = - gfAddrOf notin flags and fitsRegister(n.typ.skipTypes({tyVar, tyLent})) + {gfNode, gfNodeAddr} * flags == {} and + fitsRegister(n.typ.skipTypes({tyVar, tyLent})) proc genArrAccess2(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode; flags: TGenFlags) = @@ -1634,7 +1663,7 @@ proc genVarSection(c: PCtx; n: PNode) = c.globals.add(sa) s.position = c.globals.len if a.sons[2].kind != nkEmpty: - let tmp = c.genx(a.sons[0], {gfAddrOf}) + let tmp = c.genx(a.sons[0], {gfNodeAddr}) let val = c.genx(a.sons[2]) c.genAdditionalCopy(a.sons[2], opcWrDeref, tmp, 0, val) c.freeTemp(val) @@ -1839,8 +1868,8 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = of nkDotExpr: genObjAccess(c, n, dest, flags) of nkCheckedFieldExpr: genCheckedObjAccess(c, n, dest, flags) of nkBracketExpr: genArrAccess(c, n, dest, flags) - of nkDerefExpr, nkHiddenDeref: genAddrDeref(c, n, dest, opcLdDeref, flags) - of nkAddr, nkHiddenAddr: genAddrDeref(c, n, dest, opcAddrNode, flags) + of nkDerefExpr, nkHiddenDeref: genDeref(c, n, dest, flags) + of nkAddr, nkHiddenAddr: genAddr(c, n, dest, flags) of nkIfStmt, nkIfExpr: genIf(c, n, dest) of nkWhenStmt: # This is "when nimvm" node. Chose the first branch. diff --git a/config/nimdoc.cfg b/config/nimdoc.cfg index 6b6ec2d83..96e91283a 100644 --- a/config/nimdoc.cfg +++ b/config/nimdoc.cfg @@ -55,6 +55,8 @@ doc.item.toc = """ # HTML rendered for doc.item's seeSrc variable. Note that this will render to # the empty string if you don't pass anything through --docSeeSrcURL. Available # substitutaion variables here are: +# * $commit: branch/commit to use in source link. +# * $devel: branch to use in edit link. # * $path: relative path to the file being processed. # * $line: line of the item in the original source file. # * $url: whatever you did pass through the --docSeeSrcUrl switch (which also @@ -62,7 +64,7 @@ doc.item.toc = """ doc.item.seesrc = """ <a href="${url}/tree/${commit}/${path}#L${line}" class="link-seesrc" target="_blank">Source</a> -<a href="${url}/edit/devel/${path}#L${line}" class="link-seesrc" target="_blank" >Edit</a> +<a href="${url}/edit/${devel}/${path}#L${line}" class="link-seesrc" target="_blank" >Edit</a> """ doc.toc = """ diff --git a/doc/manual.rst b/doc/manual.rst index 7298b02a3..abdc4ce69 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -6229,10 +6229,10 @@ imported: :test: "nim c $1" :status: 1 - import strutils except `%`, toUpper + import strutils except `%`, toUpperAscii # doesn't work then: - echo "$1" % "abc".toUpper + echo "$1" % "abc".toUpperAscii It is not checked that the ``except`` list is really exported from the module. @@ -6261,24 +6261,24 @@ A module alias can be introduced via the ``as`` keyword: echo su.format("$1", "lalelu") -The original module name is then not accessible. The -notations ``path/to/module`` or ``path.to.module`` or ``"path/to/module"`` -can be used to refer to a module in subdirectories: +The original module name is then not accessible. The notations +``path/to/module`` or ``"path/to/module"`` can be used to refer to a module +in subdirectories: .. code-block:: nim - import lib.pure.strutils, lib/pure/os, "lib/pure/times" + import lib/pure/os, "lib/pure/times" -Note that the module name is still ``strutils`` and not ``lib.pure.strutils`` +Note that the module name is still ``strutils`` and not ``lib/pure/strutils`` and so one **cannot** do: .. code-block:: nim - import lib.pure.strutils - echo lib.pure.strutils + import lib/pure/strutils + echo lib/pure/strutils.toUpperAscii("abc") Likewise the following does not make sense as the name is ``strutils`` already: .. code-block:: nim - import lib.pure.strutils as strutils + import lib/pure/strutils as strutils Collective imports from a directory @@ -6297,7 +6297,8 @@ name is not a valid Nim identifier it needs to be a string literal: Pseudo import/include paths ~~~~~~~~~~~~~~~~~~~~~~~~~~~ -A directory can also be a so called "pseudo directory". +A directory can also be a so called "pseudo directory". They can be used to +avoid ambiguity when there are multiple modules with the same path. There are two pseudo directories: diff --git a/doc/mytest.cfg b/doc/mytest.cfg index be1118c46..1385e493d 100644 --- a/doc/mytest.cfg +++ b/doc/mytest.cfg @@ -3,6 +3,7 @@ [Common] cc=gcc # '=' and ':' are the same +--foo="bar" # '--cc' and 'cc' are the same, 'bar' and '"bar"' are the same --verbose [Windows] diff --git a/koch.nim b/koch.nim index 9411a95de..ad6a774e8 100644 --- a/koch.nim +++ b/koch.nim @@ -50,6 +50,7 @@ Boot options: Commands for core developers: web [options] generates the website and the full documentation + (see `nimweb.nim` for cmd line options) website [options] generates only the website csource -d:release builds the C sources for installation pdf builds the PDF documentation @@ -241,7 +242,15 @@ proc zip(args: string) = exec("$# --var:version=$# --var:mingw=none --main:compiler/nim.nim zip compiler/installer.ini" % ["tools/niminst/niminst".exe, VersionAsString]) +proc ensureCleanGit() = + let (outp, status) = osproc.execCmdEx("git diff") + if outp.len != 0: + quit "Not a clean git repository; 'git diff' not empty!" + if status != 0: + quit "Not a clean git repository; 'git diff' returned non-zero!" + proc xz(args: string) = + ensureCleanGit() bundleNimbleSrc() bundleNimsuggest(false) nimexec("cc -r $2 --var:version=$1 --var:mingw=none --main:compiler/nim.nim scripts compiler/installer.ini" % diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 8a1be3720..d4e8ada0a 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -382,16 +382,24 @@ type {.deprecated: [TBindSymRule: BindSymRule].} -proc bindSym*(ident: static[string], rule: BindSymRule = brClosed): NimNode {. +proc bindSym*(ident: string | NimNode, rule: BindSymRule = brClosed): NimNode {. magic: "NBindSym", noSideEffect.} ## creates a node that binds `ident` to a symbol node. The bound symbol ## may be an overloaded symbol. + ## if `ident` is a NimNode, it must have nkIdent kind. ## If ``rule == brClosed`` either an ``nkClosedSymChoice`` tree is ## returned or ``nkSym`` if the symbol is not ambiguous. ## If ``rule == brOpen`` either an ``nkOpenSymChoice`` tree is ## returned or ``nkSym`` if the symbol is not ambiguous. ## If ``rule == brForceOpen`` always an ``nkOpenSymChoice`` tree is ## returned even if the symbol is not ambiguous. + ## + ## experimental feature: + ## use {.experimental: "dynamicBindSym".} to activate it + ## if called from template / regular code, `ident` and `rule` must be + ## constant expression / literal value. + ## if called from macros / compile time procs / static blocks, + ## `ident` and `rule` can be VM computed value. proc genSym*(kind: NimSymKind = nskLet; ident = ""): NimNode {. magic: "NGenSym", noSideEffect.} @@ -425,6 +433,7 @@ proc getColumn(arg: NimNode): int {.magic: "NLineInfo", noSideEffect.} proc getFile(arg: NimNode): string {.magic: "NLineInfo", noSideEffect.} proc lineInfoObj*(n: NimNode): LineInfo {.compileTime.} = + ## returns ``LineInfo`` of ``n``, using absolute path for ``filename`` result.filename = n.getFile result.line = n.getLine result.column = n.getColumn @@ -1284,7 +1293,7 @@ proc customPragmaNode(n: NimNode): NimNode = let typ = n.getTypeInst() - if typ.kind == nnkBracketExpr and typ.len > 1 and typ[1].kind == nnkProcTy: + if typ.kind == nnkBracketExpr and typ.len > 1 and typ[1].kind == nnkProcTy: return typ[1][1] elif typ.typeKind == ntyTypeDesc: let impl = typ[1].getImpl() @@ -1319,6 +1328,8 @@ proc customPragmaNode(n: NimNode): NimNode = if identDefs.kind == nnkRecCase: identDefsStack.add(identDefs[0]) for i in 1..<identDefs.len: + # if it is and empty branch, skip + if identDefs[i][0].kind == nnkNilLit: continue if identDefs[i][1].kind == nnkIdentDefs: identDefsStack.add(identDefs[i][1]) else: # nnkRecList diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index ef456f093..43a429a17 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -449,10 +449,11 @@ proc generateSymbolIndex(symbols: seq[IndexEntry]): string = desc = if not symbols[j].linkDesc.isNil: symbols[j].linkDesc else: "" if desc.len > 0: result.addf("""<li><a class="reference external" - title="$3" href="$1">$2</a></li> + title="$3" data-doc-search-tag="$2" href="$1">$2</a></li> """, [url, text, desc]) else: - result.addf("""<li><a class="reference external" href="$1">$2</a></li> + result.addf("""<li><a class="reference external" + data-doc-search-tag="$2" href="$1">$2</a></li> """, [url, text]) inc j result.add("</ul></dd>\n") @@ -493,6 +494,7 @@ proc generateDocumentationTOC(entries: seq[IndexEntry]): string = # Build a list of levels and extracted titles to make processing easier. var titleRef: string + titleTag: string levels: seq[tuple[level: int, text: string]] L = 0 level = 1 @@ -519,10 +521,12 @@ proc generateDocumentationTOC(entries: seq[IndexEntry]): string = let link = entries[L].link if link.isDocumentationTitle: titleRef = link + titleTag = levels[L].text else: result.add(level.indentToLevel(levels[L].level)) - result.add("<li><a href=\"" & link & "\">" & - levels[L].text & "</a></li>\n") + result.addf("""<li><a class="reference" data-doc-search-tag="$1" href="$2"> + $3</a></li> + """, [titleTag & " : " & levels[L].text, link, levels[L].text]) inc L result.add(level.indentToLevel(1) & "</ul>\n") assert(not titleRef.isNil, diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim index 44c59c627..db33e41af 100644 --- a/lib/pure/collections/sequtils.nim +++ b/lib/pure/collections/sequtils.nim @@ -854,20 +854,20 @@ when isMainModule: doAssert numbers.distribute(6)[0] == @[1, 2] doAssert numbers.distribute(6)[5] == @[7] let a = @[1, 2, 3, 4, 5, 6, 7] - doAssert a.distribute(1, true) == @[@[1, 2, 3, 4, 5, 6, 7]] - doAssert a.distribute(1, false) == @[@[1, 2, 3, 4, 5, 6, 7]] - doAssert a.distribute(2, true) == @[@[1, 2, 3, 4], @[5, 6, 7]] - doAssert a.distribute(2, false) == @[@[1, 2, 3, 4], @[5, 6, 7]] - doAssert a.distribute(3, true) == @[@[1, 2, 3], @[4, 5], @[6, 7]] - doAssert a.distribute(3, false) == @[@[1, 2, 3], @[4, 5, 6], @[7]] - doAssert a.distribute(4, true) == @[@[1, 2], @[3, 4], @[5, 6], @[7]] - doAssert a.distribute(4, false) == @[@[1, 2], @[3, 4], @[5, 6], @[7]] - doAssert a.distribute(5, true) == @[@[1, 2], @[3, 4], @[5], @[6], @[7]] - doAssert a.distribute(5, false) == @[@[1, 2], @[3, 4], @[5, 6], @[7], @[]] - doAssert a.distribute(6, true) == @[@[1, 2], @[3], @[4], @[5], @[6], @[7]] - doAssert a.distribute(6, false) == @[ + doAssert a.distribute(1, true) == @[@[1, 2, 3, 4, 5, 6, 7]] + doAssert a.distribute(1, false) == @[@[1, 2, 3, 4, 5, 6, 7]] + doAssert a.distribute(2, true) == @[@[1, 2, 3, 4], @[5, 6, 7]] + doAssert a.distribute(2, false) == @[@[1, 2, 3, 4], @[5, 6, 7]] + doAssert a.distribute(3, true) == @[@[1, 2, 3], @[4, 5], @[6, 7]] + doAssert a.distribute(3, false) == @[@[1, 2, 3], @[4, 5, 6], @[7]] + doAssert a.distribute(4, true) == @[@[1, 2], @[3, 4], @[5, 6], @[7]] + doAssert a.distribute(4, false) == @[@[1, 2], @[3, 4], @[5, 6], @[7]] + doAssert a.distribute(5, true) == @[@[1, 2], @[3, 4], @[5], @[6], @[7]] + doAssert a.distribute(5, false) == @[@[1, 2], @[3, 4], @[5, 6], @[7], @[]] + doAssert a.distribute(6, true) == @[@[1, 2], @[3], @[4], @[5], @[6], @[7]] + doAssert a.distribute(6, false) == @[ @[1, 2], @[3, 4], @[5, 6], @[7], @[], @[]] - doAssert a.distribute(8, false) == a.distribute(8, true) + doAssert a.distribute(8, false) == a.distribute(8, true) doAssert a.distribute(90, false) == a.distribute(90, true) var b = @[0] for f in 1 .. 25: b.add(f) diff --git a/lib/pure/collections/sets.nim b/lib/pure/collections/sets.nim index 59c90bc2b..fdc3b4b03 100644 --- a/lib/pure/collections/sets.nim +++ b/lib/pure/collections/sets.nim @@ -347,6 +347,18 @@ proc excl*[A](s: var HashSet[A], other: HashSet[A]) = assert other.isValid, "The set `other` needs to be initialized." for item in other: discard exclImpl(s, item) +proc pop*[A](s: var HashSet[A]): A = + ## Remove and return an arbitrary element from the set `s`. + ## + ## Raises KeyError if the set `s` is empty. + ## + for h in 0..high(s.data): + if isFilled(s.data[h].hcode): + result = s.data[h].key + excl(s, result) + return result + raise newException(KeyError, "set is empty") + proc containsOrIncl*[A](s: var HashSet[A], key: A): bool = ## Includes `key` in the set `s` and tells if `key` was added to `s`. ## diff --git a/lib/pure/encodings.nim b/lib/pure/encodings.nim index c67cd7579..3c1cf73f4 100644 --- a/lib/pure/encodings.nim +++ b/lib/pure/encodings.nim @@ -332,7 +332,7 @@ when defined(windows): if s.len == 0: return "" # educated guess of capacity: var cap = s.len + s.len shr 2 - result = newStringOfCap(cap*2) + result = newString(cap*2) # convert to utf-16 LE var m = multiByteToWideChar(codePage = c.src, dwFlags = 0'i32, lpMultiByteStr = cstring(s), @@ -347,7 +347,7 @@ when defined(windows): lpWideCharStr = nil, cchWideChar = cint(0)) # and do the conversion properly: - result = newStringOfCap(cap*2) + result = newString(cap*2) m = multiByteToWideChar(codePage = c.src, dwFlags = 0'i32, lpMultiByteStr = cstring(s), cbMultiByte = cint(s.len), @@ -364,7 +364,7 @@ when defined(windows): if int(c.dest) == 1200: return # otherwise the fun starts again: cap = s.len + s.len shr 2 - var res = newStringOfCap(cap) + var res = newString(cap) m = wideCharToMultiByte( codePage = c.dest, dwFlags = 0'i32, @@ -382,7 +382,7 @@ when defined(windows): lpMultiByteStr = nil, cbMultiByte = cint(0)) # and do the conversion properly: - res = newStringOfCap(cap) + res = newString(cap) m = wideCharToMultiByte( codePage = c.dest, dwFlags = 0'i32, diff --git a/lib/pure/includes/oserr.nim b/lib/pure/includes/oserr.nim index eb350cbd4..493e8e174 100644 --- a/lib/pure/includes/oserr.nim +++ b/lib/pure/includes/oserr.nim @@ -60,7 +60,7 @@ proc raiseOSError*(errorCode: OSErrorCode; additionalInfo = "") {.noinline.} = if additionalInfo.len == 0: e.msg = osErrorMsg(errorCode) else: - e.msg = osErrorMsg(errorCode) & "\nAdditional info: " & additionalInfo + e.msg = osErrorMsg(errorCode) & "\nAdditional info: '" & additionalInfo & "'" if e.msg == "": e.msg = "unknown OS error" raise e diff --git a/lib/pure/ioselects/ioselectors_epoll.nim b/lib/pure/ioselects/ioselectors_epoll.nim index 36145abc7..8b3f14f34 100644 --- a/lib/pure/ioselects/ioselectors_epoll.nim +++ b/lib/pure/ioselects/ioselectors_epoll.nim @@ -92,6 +92,9 @@ proc newSelector*[T](): Selector[T] = result.maxFD = maxFD result.fds = newSeq[SelectorKey[T]](maxFD) + for i in 0 ..< maxFD: + result.fds[i].ident = InvalidIdent + proc close*[T](s: Selector[T]) = let res = posix.close(s.epollFD) when hasThreadSupport: @@ -100,12 +103,6 @@ proc close*[T](s: Selector[T]) = if res != 0: raiseIOSelectorsError(osLastError()) -template clearKey[T](key: ptr SelectorKey[T]) = - var empty: T - key.ident = 0 - key.events = {} - key.data = empty - proc newSelectEvent*(): SelectEvent = let fdci = eventfd(0, 0) if fdci == -1: @@ -135,7 +132,7 @@ proc registerHandle*[T](s: Selector[T], fd: int | SocketHandle, events: set[Event], data: T) = let fdi = int(fd) s.checkFd(fdi) - doAssert(s.fds[fdi].ident == 0, "Descriptor $# already registered" % $fdi) + doAssert(s.fds[fdi].ident == InvalidIdent, "Descriptor $# already registered" % $fdi) s.setKey(fdi, events, 0, data) if events != {}: var epv = EpollEvent(events: EPOLLRDHUP) @@ -152,7 +149,7 @@ proc updateHandle*[T](s: Selector[T], fd: int | SocketHandle, events: set[Event] let fdi = int(fd) s.checkFd(fdi) var pkey = addr(s.fds[fdi]) - doAssert(pkey.ident != 0, + doAssert(pkey.ident != InvalidIdent, "Descriptor $# is not registered in the selector!" % $fdi) doAssert(pkey.events * maskEvents == {}) if pkey.events != events: @@ -180,7 +177,7 @@ proc unregister*[T](s: Selector[T], fd: int|SocketHandle) = let fdi = int(fd) s.checkFd(fdi) var pkey = addr(s.fds[fdi]) - doAssert(pkey.ident != 0, + doAssert(pkey.ident != InvalidIdent, "Descriptor $# is not registered in the selector!" % $fdi) if pkey.events != {}: when not defined(android): @@ -243,7 +240,7 @@ proc unregister*[T](s: Selector[T], ev: SelectEvent) = let fdi = int(ev.efd) s.checkFd(fdi) var pkey = addr(s.fds[fdi]) - doAssert(pkey.ident != 0, "Event is not registered in the queue!") + doAssert(pkey.ident != InvalidIdent, "Event is not registered in the queue!") doAssert(Event.User in pkey.events) var epv = EpollEvent() if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fdi.cint, addr epv) != 0: @@ -262,7 +259,7 @@ proc registerTimer*[T](s: Selector[T], timeout: int, oneshot: bool, setNonBlocking(fdi.cint) s.checkFd(fdi) - doAssert(s.fds[fdi].ident == 0) + doAssert(s.fds[fdi].ident == InvalidIdent) var events = {Event.Timer} var epv = EpollEvent(events: EPOLLIN or EPOLLRDHUP) @@ -307,7 +304,7 @@ when not defined(android): setNonBlocking(fdi.cint) s.checkFd(fdi) - doAssert(s.fds[fdi].ident == 0) + doAssert(s.fds[fdi].ident == InvalidIdent) var epv = EpollEvent(events: EPOLLIN or EPOLLRDHUP) epv.data.u64 = fdi.uint @@ -334,7 +331,7 @@ when not defined(android): setNonBlocking(fdi.cint) s.checkFd(fdi) - doAssert(s.fds[fdi].ident == 0) + doAssert(s.fds[fdi].ident == InvalidIdent) var epv = EpollEvent(events: EPOLLIN or EPOLLRDHUP) epv.data.u64 = fdi.uint @@ -347,7 +344,7 @@ when not defined(android): proc registerEvent*[T](s: Selector[T], ev: SelectEvent, data: T) = let fdi = int(ev.efd) - doAssert(s.fds[fdi].ident == 0, "Event is already registered in the queue!") + doAssert(s.fds[fdi].ident == InvalidIdent, "Event is already registered in the queue!") s.setKey(fdi, {Event.User}, 0, data) var epv = EpollEvent(events: EPOLLIN or EPOLLRDHUP) epv.data.u64 = ev.efd.uint @@ -381,7 +378,7 @@ proc selectInto*[T](s: Selector[T], timeout: int, let fdi = int(resTable[i].data.u64) let pevents = resTable[i].events var pkey = addr(s.fds[fdi]) - doAssert(pkey.ident != 0) + doAssert(pkey.ident != InvalidIdent) var rkey = ReadyKey(fd: fdi, events: {}) if (pevents and EPOLLERR) != 0 or (pevents and EPOLLHUP) != 0: @@ -482,7 +479,7 @@ template isEmpty*[T](s: Selector[T]): bool = (s.count == 0) proc contains*[T](s: Selector[T], fd: SocketHandle|int): bool {.inline.} = - return s.fds[fd.int].ident != 0 + return s.fds[fd.int].ident != InvalidIdent proc getData*[T](s: Selector[T], fd: SocketHandle|int): var T = let fdi = int(fd) diff --git a/lib/pure/ioselects/ioselectors_kqueue.nim b/lib/pure/ioselects/ioselectors_kqueue.nim index 10e23c072..142e988d0 100644 --- a/lib/pure/ioselects/ioselectors_kqueue.nim +++ b/lib/pure/ioselects/ioselectors_kqueue.nim @@ -114,6 +114,9 @@ proc newSelector*[T](): Selector[T] = result.fds = newSeq[SelectorKey[T]](maxFD) result.changes = newSeqOfCap[KEvent](MAX_KQUEUE_EVENTS) + for i in 0 ..< maxFD: + result.fds[i].ident = InvalidIdent + result.sock = usock result.kqFD = kqFD result.maxFD = maxFD.int @@ -128,12 +131,6 @@ proc close*[T](s: Selector[T]) = if res1 != 0 or res2 != 0: raiseIOSelectorsError(osLastError()) -template clearKey[T](key: ptr SelectorKey[T]) = - var empty: T - key.ident = 0 - key.events = {} - key.data = empty - proc newSelectEvent*(): SelectEvent = var fds: array[2, cint] if posix.pipe(fds) != 0: @@ -221,7 +218,7 @@ proc registerHandle*[T](s: Selector[T], fd: int | SocketHandle, events: set[Event], data: T) = let fdi = int(fd) s.checkFd(fdi) - doAssert(s.fds[fdi].ident == 0) + doAssert(s.fds[fdi].ident == InvalidIdent) s.setKey(fdi, events, 0, data) if events != {}: @@ -242,7 +239,7 @@ proc updateHandle*[T](s: Selector[T], fd: int | SocketHandle, let fdi = int(fd) s.checkFd(fdi) var pkey = addr(s.fds[fdi]) - doAssert(pkey.ident != 0, + doAssert(pkey.ident != InvalidIdent, "Descriptor $# is not registered in the queue!" % $fdi) doAssert(pkey.events * maskEvents == {}) @@ -269,7 +266,7 @@ proc registerTimer*[T](s: Selector[T], timeout: int, oneshot: bool, data: T): int {.discardable.} = let fdi = getUnique(s) s.checkFd(fdi) - doAssert(s.fds[fdi].ident == 0) + doAssert(s.fds[fdi].ident == InvalidIdent) let events = if oneshot: {Event.Timer, Event.Oneshot} else: {Event.Timer} let flags: cushort = if oneshot: EV_ONESHOT or EV_ADD else: EV_ADD @@ -291,7 +288,7 @@ proc registerSignal*[T](s: Selector[T], signal: int, data: T): int {.discardable.} = let fdi = getUnique(s) s.checkFd(fdi) - doAssert(s.fds[fdi].ident == 0) + doAssert(s.fds[fdi].ident == InvalidIdent) s.setKey(fdi, {Event.Signal}, signal, data) var nmask, omask: Sigset @@ -315,7 +312,7 @@ proc registerProcess*[T](s: Selector[T], pid: int, data: T): int {.discardable.} = let fdi = getUnique(s) s.checkFd(fdi) - doAssert(s.fds[fdi].ident == 0) + doAssert(s.fds[fdi].ident == InvalidIdent) var kflags: cushort = EV_ONESHOT or EV_ADD setKey(s, fdi, {Event.Process, Event.Oneshot}, pid, data) @@ -331,7 +328,7 @@ proc registerProcess*[T](s: Selector[T], pid: int, proc registerEvent*[T](s: Selector[T], ev: SelectEvent, data: T) = let fdi = ev.rfd.int - doAssert(s.fds[fdi].ident == 0, "Event is already registered in the queue!") + doAssert(s.fds[fdi].ident == InvalidIdent, "Event is already registered in the queue!") setKey(s, fdi, {Event.User}, 0, data) modifyKQueue(s, fdi.uint, EVFILT_READ, EV_ADD, 0, 0, nil) @@ -374,7 +371,7 @@ proc unregister*[T](s: Selector[T], fd: int|SocketHandle) = let fdi = int(fd) s.checkFd(fdi) var pkey = addr(s.fds[fdi]) - doAssert(pkey.ident != 0, + doAssert(pkey.ident != InvalidIdent, "Descriptor [" & $fdi & "] is not registered in the queue!") if pkey.events != {}: @@ -434,7 +431,7 @@ proc unregister*[T](s: Selector[T], ev: SelectEvent) = let fdi = int(ev.rfd) s.checkFd(fdi) var pkey = addr(s.fds[fdi]) - doAssert(pkey.ident != 0, "Event is not registered in the queue!") + doAssert(pkey.ident != InvalidIdent, "Event is not registered in the queue!") doAssert(Event.User in pkey.events) modifyKQueue(s, uint(fdi), EVFILT_READ, EV_DELETE, 0, 0, nil) when not declared(CACHE_EVENTS): @@ -593,7 +590,7 @@ template isEmpty*[T](s: Selector[T]): bool = (s.count == 0) proc contains*[T](s: Selector[T], fd: SocketHandle|int): bool {.inline.} = - return s.fds[fd.int].ident != 0 + return s.fds[fd.int].ident != InvalidIdent proc getData*[T](s: Selector[T], fd: SocketHandle|int): var T = let fdi = int(fd) diff --git a/lib/pure/ioselects/ioselectors_poll.nim b/lib/pure/ioselects/ioselectors_poll.nim index c36750c8d..9d708b0c1 100644 --- a/lib/pure/ioselects/ioselectors_poll.nim +++ b/lib/pure/ioselects/ioselectors_poll.nim @@ -70,6 +70,9 @@ proc newSelector*[T](): Selector[T] = result.fds = newSeq[SelectorKey[T]](maxFD) result.pollfds = newSeq[TPollFd](maxFD) + for i in 0 ..< maxFD: + result.fds[i].ident = InvalidIdent + proc close*[T](s: Selector[T]) = when hasThreadSupport: deinitLock(s.lock) @@ -77,12 +80,6 @@ proc close*[T](s: Selector[T]) = deallocSharedArray(s.pollfds) deallocShared(cast[pointer](s)) -template clearKey[T](key: ptr SelectorKey[T]) = - var empty: T - key.ident = 0 - key.events = {} - key.data = empty - template pollAdd[T](s: Selector[T], sock: cint, events: set[Event]) = withPollLock(s): var pollev: cshort = 0 @@ -135,7 +132,7 @@ proc registerHandle*[T](s: Selector[T], fd: int | SocketHandle, events: set[Event], data: T) = var fdi = int(fd) s.checkFd(fdi) - doAssert(s.fds[fdi].ident == 0) + doAssert(s.fds[fdi].ident == InvalidIdent) setKey(s, fdi, events, 0, data) if events != {}: s.pollAdd(fdi.cint, events) @@ -146,7 +143,7 @@ proc updateHandle*[T](s: Selector[T], fd: int | SocketHandle, let fdi = int(fd) s.checkFd(fdi) var pkey = addr(s.fds[fdi]) - doAssert(pkey.ident != 0, + doAssert(pkey.ident != InvalidIdent, "Descriptor [" & $fdi & "] is not registered in the queue!") doAssert(pkey.events * maskEvents == {}) @@ -162,7 +159,7 @@ proc updateHandle*[T](s: Selector[T], fd: int | SocketHandle, proc registerEvent*[T](s: Selector[T], ev: SelectEvent, data: T) = var fdi = int(ev.rfd) - doAssert(s.fds[fdi].ident == 0, "Event is already registered in the queue!") + doAssert(s.fds[fdi].ident == InvalidIdent, "Event is already registered in the queue!") var events = {Event.User} setKey(s, fdi, events, 0, data) events.incl(Event.Read) @@ -172,9 +169,9 @@ proc unregister*[T](s: Selector[T], fd: int|SocketHandle) = let fdi = int(fd) s.checkFd(fdi) var pkey = addr(s.fds[fdi]) - doAssert(pkey.ident != 0, + doAssert(pkey.ident != InvalidIdent, "Descriptor [" & $fdi & "] is not registered in the queue!") - pkey.ident = 0 + pkey.ident = InvalidIdent pkey.events = {} s.pollRemove(fdi.cint) @@ -182,9 +179,9 @@ proc unregister*[T](s: Selector[T], ev: SelectEvent) = let fdi = int(ev.rfd) s.checkFd(fdi) var pkey = addr(s.fds[fdi]) - doAssert(pkey.ident != 0, "Event is not registered in the queue!") + doAssert(pkey.ident != InvalidIdent, "Event is not registered in the queue!") doAssert(Event.User in pkey.events) - pkey.ident = 0 + pkey.ident = InvalidIdent pkey.events = {} s.pollRemove(fdi.cint) @@ -270,7 +267,7 @@ template isEmpty*[T](s: Selector[T]): bool = (s.count == 0) proc contains*[T](s: Selector[T], fd: SocketHandle|int): bool {.inline.} = - return s.fds[fd.int].ident != 0 + return s.fds[fd.int].ident != InvalidIdent proc getData*[T](s: Selector[T], fd: SocketHandle|int): var T = let fdi = int(fd) diff --git a/lib/pure/ioselects/ioselectors_select.nim b/lib/pure/ioselects/ioselectors_select.nim index 7ed250307..cd6a72b44 100644 --- a/lib/pure/ioselects/ioselectors_select.nim +++ b/lib/pure/ioselects/ioselectors_select.nim @@ -99,6 +99,9 @@ proc newSelector*[T](): Selector[T] = result = Selector[T]() result.fds = newSeq[SelectorKey[T]](FD_SETSIZE) + for i in 0 ..< FD_SETSIZE: + result.fds[i].ident = InvalidIdent + IOFD_ZERO(addr result.rSet) IOFD_ZERO(addr result.wSet) IOFD_ZERO(addr result.eSet) @@ -195,7 +198,7 @@ proc setSelectKey[T](s: Selector[T], fd: SocketHandle, events: set[Event], var i = 0 let fdi = int(fd) while i < FD_SETSIZE: - if s.fds[i].ident == 0: + if s.fds[i].ident == InvalidIdent: var pkey = addr(s.fds[i]) pkey.ident = fdi pkey.events = events @@ -221,7 +224,7 @@ proc delKey[T](s: Selector[T], fd: SocketHandle) = var i = 0 while i < FD_SETSIZE: if s.fds[i].ident == fd.int: - s.fds[i].ident = 0 + s.fds[i].ident = InvalidIdent s.fds[i].events = {} s.fds[i].data = empty break @@ -335,7 +338,7 @@ proc selectInto*[T](s: Selector[T], timeout: int, var k = 0 while (i < FD_SETSIZE) and (k < count): - if s.fds[i].ident != 0: + if s.fds[i].ident != InvalidIdent: var flag = false var pkey = addr(s.fds[i]) var rkey = ReadyKey(fd: int(pkey.ident), events: {}) diff --git a/lib/pure/json.nim b/lib/pure/json.nim index 67f92dffe..b9279b18c 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -1004,6 +1004,13 @@ proc processElseBranch(recCaseNode, elseBranch, jsonNode, kindType, exprColonExpr.add(ifStmt) proc createConstructor(typeSym, jsonNode: NimNode): NimNode {.compileTime.} + +proc detectDistinctType(typeSym: NimNode): NimNode = + let + typeImpl = getTypeImpl(typeSym) + typeInst = getTypeInst(typeSym) + result = if typeImpl.typeKind == ntyDistinct: typeImpl else: typeInst + proc processObjField(field, jsonNode: NimNode): seq[NimNode] = ## Process a field from a ``RecList``. ## @@ -1022,8 +1029,8 @@ proc processObjField(field, jsonNode: NimNode): seq[NimNode] = # Add the field value. # -> jsonNode["`field`"] let indexedJsonNode = createJsonIndexer(jsonNode, $field) - exprColonExpr.add(createConstructor(getTypeInst(field), indexedJsonNode)) - + let typeNode = detectDistinctType(field) + exprColonExpr.add(createConstructor(typeNode, indexedJsonNode)) of nnkRecCase: # A "case" field that introduces a variant. let exprColonExpr = newNimNode(nnkExprColonExpr) @@ -1248,7 +1255,7 @@ proc createConstructor(typeSym, jsonNode: NimNode): NimNode = let seqT = typeSym[1] let forLoopI = genSym(nskForVar, "i") let indexerNode = createJsonIndexer(jsonNode, forLoopI) - let constructorNode = createConstructor(seqT, indexerNode) + let constructorNode = createConstructor(detectDistinctType(seqT), indexerNode) # Create a statement expression containing a for loop. result = quote do: @@ -1284,7 +1291,10 @@ proc createConstructor(typeSym, jsonNode: NimNode): NimNode = # Handle all other types. let obj = getType(typeSym) - if obj.kind == nnkBracketExpr: + let typeNode = getTypeImpl(typeSym) + if typeNode.typeKind == ntyDistinct: + result = createConstructor(typeNode, jsonNode) + elif obj.kind == nnkBracketExpr: # When `Sym "Foo"` turns out to be a `ref object`. result = createConstructor(obj, jsonNode) else: @@ -1295,6 +1305,21 @@ proc createConstructor(typeSym, jsonNode: NimNode): NimNode = # TODO: The fact that `jsonNode` here works to give a good line number # is weird. Specifying typeSym should work but doesn't. error("Use a named tuple instead of: " & $toStrLit(typeSym), jsonNode) + of nnkDistinctTy: + var baseType = typeSym + # solve nested distinct types + while baseType.typeKind == ntyDistinct: + let impl = getTypeImpl(baseType[0]) + if impl.typeKind != ntyDistinct: + baseType = baseType[0] + break + baseType = impl + let ret = createConstructor(baseType, jsonNode) + let typeInst = getTypeInst(typeSym) + result = quote do: + ( + `typeInst`(`ret`) + ) else: doAssert false, "Unable to create constructor for: " & $typeSym.kind @@ -1418,7 +1443,7 @@ macro to*(node: JsonNode, T: typedesc): untyped = ## doAssert data.person.age == 21 ## doAssert data.list == @[1, 2, 3, 4] - let typeNode = getTypeInst(T) + let typeNode = getTypeImpl(T) expectKind(typeNode, nnkBracketExpr) doAssert(($typeNode[0]).normalize == "typedesc") diff --git a/lib/pure/mimetypes.nim b/lib/pure/mimetypes.nim index ff69ba61e..8f5f3a183 100644 --- a/lib/pure/mimetypes.nim +++ b/lib/pure/mimetypes.nim @@ -231,6 +231,7 @@ const mimes* = { "xcf": "application/x-xcf", "fig": "application/x-xfig", "xpi": "application/x-xpinstall", + "wasm": "application/wasm", "amr": "audio/amr", "awb": "audio/amr-wb", "amr": "audio/amr", diff --git a/lib/pure/net.nim b/lib/pure/net.nim index 5d2efebee..771e7de10 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -41,7 +41,7 @@ ## immediately. ## ## .. code-block:: Nim -## var socket = newSocket() +## var socket = newSocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP) ## socket.sendTo("192.168.0.1", Port(27960), "status\n") ## ## Creating a server diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 84f492c9d..9cc83c372 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -971,7 +971,7 @@ proc rawCreateDir(dir: string): bool = elif errno in {EEXIST, ENOSYS}: result = false else: - raiseOSError(osLastError()) + raiseOSError(osLastError(), dir) elif defined(posix): let res = mkdir(dir, 0o777) if res == 0'i32: @@ -980,7 +980,7 @@ proc rawCreateDir(dir: string): bool = result = false else: #echo res - raiseOSError(osLastError()) + raiseOSError(osLastError(), dir) else: when useWinUnicode: wrapUnary(res, createDirectoryW, dir) @@ -992,7 +992,7 @@ proc rawCreateDir(dir: string): bool = elif getLastError() == 183'i32: result = false else: - raiseOSError(osLastError()) + raiseOSError(osLastError(), dir) proc existsOrCreateDir*(dir: string): bool {.rtl, extern: "nos$1", tags: [WriteDirEffect, ReadDirEffect].} = @@ -1005,7 +1005,7 @@ proc existsOrCreateDir*(dir: string): bool {.rtl, extern: "nos$1", if result: # path already exists - need to check that it is indeed a directory if not existsDir(dir): - raise newException(IOError, "Failed to create the directory") + raise newException(IOError, "Failed to create '" & dir & "'") proc createDir*(dir: string) {.rtl, extern: "nos$1", tags: [WriteDirEffect, ReadDirEffect].} = diff --git a/lib/pure/ospaths.nim b/lib/pure/ospaths.nim index 4ae5afd6c..a7ebd9d15 100644 --- a/lib/pure/ospaths.nim +++ b/lib/pure/ospaths.nim @@ -147,7 +147,7 @@ else: # UNIX-like operating system DirSep* = '/' AltSep* = DirSep PathSep* = ':' - FileSystemCaseSensitive* = true + FileSystemCaseSensitive* = when defined(macosx): false else: true ExeExt* = "" ScriptExt* = "" DynlibFormat* = when defined(macosx): "lib$1.dylib" else: "lib$1.so" @@ -410,6 +410,11 @@ proc cmpPaths*(pathA, pathB: string): int {. ## | 0 iff pathA == pathB ## | < 0 iff pathA < pathB ## | > 0 iff pathA > pathB + runnableExamples: + when defined(macosx): + doAssert cmpPaths("foo", "Foo") == 0 + elif defined(posix): + doAssert cmpPaths("foo", "Foo") > 0 if FileSystemCaseSensitive: result = cmp(pathA, pathB) else: diff --git a/lib/pure/random.nim b/lib/pure/random.nim index 01ea9c845..e565fccf8 100644 --- a/lib/pure/random.nim +++ b/lib/pure/random.nim @@ -110,7 +110,7 @@ proc random*[T](a: openArray[T]): T {.deprecated.} = ## Use ``rand`` instead. result = a[random(a.low..a.len)] -proc rand*(r: var Rand; max: int): int {.benign.} = +proc rand*(r: var Rand; max: Natural): int {.benign.} = ## Returns a random number in the range 0..max. The sequence of ## random number is always the same, unless `randomize` is called ## which initializes the random number generator with a "random" @@ -128,7 +128,7 @@ proc rand*(max: int): int {.benign.} = ## number, i.e. a tickcount. rand(state, max) -proc rand*(r: var Rand; max: float): float {.benign.} = +proc rand*(r: var Rand; max: range[0.0 .. high(float)]): float {.benign.} = ## Returns a random number in the range 0..max. The sequence of ## random number is always the same, unless `randomize` is called ## which initializes the random number generator with a "random" @@ -218,4 +218,17 @@ when isMainModule: doAssert rand(0) == 0 doAssert rand("a") == 'a' + when compileOption("rangeChecks"): + try: + discard rand(-1) + doAssert false + except RangeError: + discard + + try: + discard rand(-1.0) + doAssert false + except RangeError: + discard + main() diff --git a/lib/pure/selectors.nim b/lib/pure/selectors.nim index 935250e08..e4c2b2124 100644 --- a/lib/pure/selectors.nim +++ b/lib/pure/selectors.nim @@ -261,6 +261,9 @@ else: param: int data: T + const + InvalidIdent = -1 + proc raiseIOSelectorsError[T](message: T) = var msg = "" when T is string: @@ -302,6 +305,12 @@ else: if posix.sigprocmask(SIG_UNBLOCK, newmask, oldmask) == -1: raiseIOSelectorsError(osLastError()) + template clearKey[T](key: ptr SelectorKey[T]) = + var empty: T + key.ident = InvalidIdent + key.events = {} + key.data = empty + when defined(linux): include ioselects/ioselectors_epoll elif bsdPlatform: diff --git a/lib/pure/times.nim b/lib/pure/times.nim index bc4de7ee4..cdb7a4466 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -181,10 +181,27 @@ elif defined(windows): type Month* = enum ## Represents a month. Note that the enum starts at ``1``, so ``ord(month)`` will give ## the month number in the range ``[1..12]``. - mJan = 1, mFeb, mMar, mApr, mMay, mJun, mJul, mAug, mSep, mOct, mNov, mDec + mJan = (1, "January") + mFeb = "February" + mMar = "March" + mApr = "April" + mMay = "May" + mJun = "June" + mJul = "July" + mAug = "August" + mSep = "September" + mOct = "October" + mNov = "November" + mDec = "December" WeekDay* = enum ## Represents a weekday. - dMon, dTue, dWed, dThu, dFri, dSat, dSun + dMon = "Monday" + dTue = "Tuesday" + dWed = "Wednesday" + dThu = "Thursday" + dFri = "Friday" + dSat = "Saturday" + dSun = "Sunday" MonthdayRange* = range[1..31] HourRange* = range[0..23] @@ -1074,20 +1091,6 @@ proc getClockStr*(): string {.rtl, extern: "nt$1", tags: [TimeEffect].} = result = intToStr(ti.hour, 2) & ':' & intToStr(ti.minute, 2) & ':' & intToStr(ti.second, 2) -proc `$`*(day: WeekDay): string = - ## Stringify operator for ``WeekDay``. - const lookup: array[WeekDay, string] = ["Monday", "Tuesday", "Wednesday", - "Thursday", "Friday", "Saturday", "Sunday"] - return lookup[day] - -proc `$`*(m: Month): string = - ## Stringify operator for ``Month``. - const lookup: array[Month, string] = ["January", "February", "March", - "April", "May", "June", "July", "August", "September", "October", - "November", "December"] - return lookup[m] - - proc toParts* (ti: TimeInterval): TimeIntervalParts = ## Converts a `TimeInterval` into an array consisting of its time units, ## starting with nanoseconds and ending with years diff --git a/lib/system.nim b/lib/system.nim index 29e357c68..c033a632b 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1479,8 +1479,8 @@ when defined(nimdoc): ## ## Note that this is a *runtime* call and using ``quit`` inside a macro won't ## have any compile time effect. If you need to stop the compiler inside a - ## macro, use the `error <manual.html#error-pragma>`_ or `fatal - ## <manual.html#fatal-pragma>`_ pragmas. + ## macro, use the `error <manual.html#pragmas-error-pragma>`_ or `fatal + ## <manual.html#pragmas-fatal-pragma>`_ pragmas. elif defined(genode): include genode/env @@ -2438,7 +2438,7 @@ iterator fieldPairs*[T: tuple|object](x: T): RootObj {. ## When you iterate over objects with different field types you have to use ## the compile time ``when`` instead of a runtime ``if`` to select the code ## you want to run for each type. To perform the comparison use the `is - ## operator <manual.html#is-operator>`_. Example: + ## operator <manual.html#generics-is-operator>`_. Example: ## ## .. code-block:: Nim ## @@ -3788,7 +3788,9 @@ template doAssert*(cond: bool, msg = "") = ## same as `assert` but is always turned on and not affected by the ## ``--assertions`` command line switch. bind instantiationInfo - {.line: instantiationInfo().}: + # NOTE: `true` is correct here; --excessiveStackTrace:on will control whether + # or not to output full paths. + {.line: instantiationInfo(-1, true).}: if not cond: raiseAssert(astToStr(cond) & ' ' & instantiationInfo(-1, false).fileName & '(' & diff --git a/lib/system/alloc.nim b/lib/system/alloc.nim index 95becde22..ffb7aaf86 100644 --- a/lib/system/alloc.nim +++ b/lib/system/alloc.nim @@ -116,6 +116,8 @@ type nextChunkSize: int bottomData: AvlNode heapLinks: HeapLinks + when defined(nimTypeNames): + allocCounter, deallocCounter: int const fsLookupTable: array[byte, int8] = [ @@ -434,8 +436,9 @@ proc requestOsChunks(a: var MemRegion, size: int): PBigChunk = a.nextChunkSize = PageSize*4 else: a.nextChunkSize = min(roundup(usedMem shr 2, PageSize), a.nextChunkSize * 2) - var size = size + a.nextChunkSize = min(a.nextChunkSize, MaxBigChunkSize) + var size = size if size > a.nextChunkSize: result = cast[PBigChunk](osAllocPages(size)) else: @@ -737,6 +740,8 @@ when false: result = nil proc rawAlloc(a: var MemRegion, requestedSize: int): pointer = + when defined(nimTypeNames): + inc(a.allocCounter) sysAssert(allocInv(a), "rawAlloc: begin") sysAssert(roundup(65, 8) == 72, "rawAlloc: roundup broken") sysAssert(requestedSize >= sizeof(FreeCell), "rawAlloc: requested size too small") @@ -810,6 +815,8 @@ proc rawAlloc0(a: var MemRegion, requestedSize: int): pointer = zeroMem(result, requestedSize) proc rawDealloc(a: var MemRegion, p: pointer) = + when defined(nimTypeNames): + inc(a.deallocCounter) #sysAssert(isAllocatedPtr(a, p), "rawDealloc: no allocated pointer") sysAssert(allocInv(a), "rawDealloc: begin") var c = pageAddr(p) @@ -975,6 +982,10 @@ proc getOccupiedMem(a: MemRegion): int {.inline.} = result = a.occ # a.currMem - a.freeMem +when defined(nimTypeNames): + proc getMemCounters(a: MemRegion): (int, int) {.inline.} = + (a.allocCounter, a.deallocCounter) + # ---------------------- thread memory region ------------------------------- template instantiateForRegion(allocator: untyped) = @@ -1018,6 +1029,9 @@ template instantiateForRegion(allocator: untyped) = proc getOccupiedMem(): int = return allocator.occ #getTotalMem() - getFreeMem() proc getMaxMem*(): int = return getMaxMem(allocator) + when defined(nimTypeNames): + proc getMemCounters*(): (int, int) = getMemCounters(allocator) + # -------------------- shared heap region ---------------------------------- when hasThreadSupport: var sharedHeap: MemRegion diff --git a/lib/system/ansi_c.nim b/lib/system/ansi_c.nim index 4d21f8747..f593d4c99 100644 --- a/lib/system/ansi_c.nim +++ b/lib/system/ansi_c.nim @@ -27,6 +27,9 @@ proc c_strcmp(a, b: cstring): cint {. importc: "strcmp", header: "<string.h>", noSideEffect.} proc c_strlen(a: cstring): csize {. importc: "strlen", header: "<string.h>", noSideEffect.} +proc c_abort() {. + importc: "abort", header: "<stdlib.h>", noSideEffect.} + when defined(linux) and defined(amd64): type diff --git a/lib/system/gc.nim b/lib/system/gc.nim index 425963f3f..74ac68eea 100644 --- a/lib/system/gc.nim +++ b/lib/system/gc.nim @@ -548,7 +548,10 @@ proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer = gcTrace(res, csAllocated) track("growObj old", ol, 0) track("growObj new", res, newsize) - when reallyDealloc: + when defined(nimIncrSeqV3): + # since we steal the old seq's contents, we set the old length to 0. + cast[PGenericSeq](old).len = 0 + elif reallyDealloc: sysAssert(allocInv(gch.region), "growObj before dealloc") if ol.refcount shr rcShift <=% 1: # free immediately to save space: diff --git a/lib/system/gc_common.nim b/lib/system/gc_common.nim index 711a610bf..dcea0c4cc 100644 --- a/lib/system/gc_common.nim +++ b/lib/system/gc_common.nim @@ -57,6 +57,9 @@ when defined(nimTypeNames): for i in 0 .. n-1: c_fprintf(stdout, "[Heap] %s: #%ld; bytes: %ld\n", a[i][0], a[i][1], a[i][2]) c_fprintf(stdout, "[Heap] total number of bytes: %ld\n", totalAllocated) + when defined(nimTypeNames): + let (allocs, deallocs) = getMemCounters() + c_fprintf(stdout, "[Heap] allocs/deallocs: %ld/%ld\n", allocs, deallocs) when defined(nimGcRefLeak): proc oomhandler() = diff --git a/lib/system/sysio.nim b/lib/system/sysio.nim index e6b8ec3a6..3168e4cb2 100644 --- a/lib/system/sysio.nim +++ b/lib/system/sysio.nim @@ -417,12 +417,18 @@ proc setStdIoUnbuffered() = discard c_setvbuf(stdin, nil, IONBF, 0) when declared(stdout): + when defined(windows) and compileOption("threads"): + var echoLock: SysLock + initSysLock echoLock + proc echoBinSafe(args: openArray[string]) {.compilerProc.} = # flockfile deadlocks some versions of Android 5.x.x when not defined(windows) and not defined(android) and not defined(nintendoswitch): proc flockfile(f: File) {.importc, noDecl.} proc funlockfile(f: File) {.importc, noDecl.} flockfile(stdout) + when defined(windows) and compileOption("threads"): + acquireSys echoLock for s in args: discard c_fwrite(s.cstring, s.len, 1, stdout) const linefeed = "\n" # can be 1 or more chars @@ -430,5 +436,7 @@ when declared(stdout): discard c_fflush(stdout) when not defined(windows) and not defined(android) and not defined(nintendoswitch): funlockfile(stdout) + when defined(windows) and compileOption("threads"): + releaseSys echoLock {.pop.} diff --git a/lib/system/sysstr.nim b/lib/system/sysstr.nim index 9a73d3f9d..951435972 100644 --- a/lib/system/sysstr.nim +++ b/lib/system/sysstr.nim @@ -144,8 +144,13 @@ proc addChar(s: NimString, c: char): NimString = result = s if result.len >= result.space: let r = resize(result.space) - result = cast[NimString](growObj(result, - sizeof(TGenericSeq) + r + 1)) + when defined(nimIncrSeqV3): + result = rawNewStringNoInit(r) + result.len = s.len + copyMem(addr result.data[0], unsafeAddr(s.data[0]), s.len+1) + else: + result = cast[NimString](growObj(result, + sizeof(TGenericSeq) + r + 1)) result.reserved = r result.data[result.len] = c result.data[result.len+1] = '\0' @@ -188,8 +193,13 @@ proc resizeString(dest: NimString, addlen: int): NimString {.compilerRtl.} = elif dest.len + addlen <= dest.space: result = dest else: # slow path: - var sp = max(resize(dest.space), dest.len + addlen) - result = cast[NimString](growObj(dest, sizeof(TGenericSeq) + sp + 1)) + let sp = max(resize(dest.space), dest.len + addlen) + when defined(nimIncrSeqV3): + result = rawNewStringNoInit(sp) + result.len = dest.len + copyMem(addr result.data[0], unsafeAddr(dest.data[0]), dest.len+1) + else: + result = cast[NimString](growObj(dest, sizeof(TGenericSeq) + sp + 1)) result.reserved = sp #result = rawNewString(sp) #copyMem(result, dest, dest.len + sizeof(TGenericSeq)) @@ -212,7 +222,15 @@ proc setLengthStr(s: NimString, newLen: int): NimString {.compilerRtl.} = elif n <= s.space: result = s else: - result = resizeString(s, n) + let sp = max(resize(s.space), newLen) + when defined(nimIncrSeqV3): + result = rawNewStringNoInit(sp) + result.len = s.len + copyMem(addr result.data[0], unsafeAddr(s.data[0]), s.len+1) + zeroMem(addr result.data[s.len], newLen - s.len) + result.reserved = sp + else: + result = resizeString(s, n) result.len = n result.data[n] = '\0' @@ -242,6 +260,9 @@ proc incrSeqV2(seq: PGenericSeq, elemSize: int): PGenericSeq {.compilerProc.} = GenericSeqSize)) result.reserved = r +template `+!`(p: pointer, s: int): pointer = + cast[pointer](cast[int](p) +% s) + proc incrSeqV3(s: PGenericSeq, typ: PNimType): PGenericSeq {.compilerProc.} = if s == nil: result = cast[PGenericSeq](newSeq(typ, 1)) @@ -250,9 +271,16 @@ proc incrSeqV3(s: PGenericSeq, typ: PNimType): PGenericSeq {.compilerProc.} = result = s if result.len >= result.space: let r = resize(result.space) - result = cast[PGenericSeq](growObj(result, typ.base.size * r + + when defined(nimIncrSeqV3): + result = cast[PGenericSeq](newSeq(typ, r)) + result.len = s.len + copyMem(result +! GenericSeqSize, s +! GenericSeqSize, s.len * typ.base.size) + # since we steal the content from 's', it's crucial to set s's len to 0. + s.len = 0 + else: + result = cast[PGenericSeq](growObj(result, typ.base.size * r + GenericSeqSize)) - result.reserved = r + result.reserved = r proc setLengthSeq(seq: PGenericSeq, elemSize, newLen: int): PGenericSeq {. compilerRtl, inl.} = @@ -296,7 +324,40 @@ proc setLengthSeq(seq: PGenericSeq, elemSize, newLen: int): PGenericSeq {. proc setLengthSeqV2(s: PGenericSeq, typ: PNimType, newLen: int): PGenericSeq {. compilerRtl.} = + sysAssert typ.kind == tySequence, "setLengthSeqV2: type is not a seq" if s == nil: result = cast[PGenericSeq](newSeq(typ, newLen)) else: - result = setLengthSeq(s, typ.base.size, newLen) + when defined(nimIncrSeqV3): + let elemSize = typ.base.size + if s.space < newLen: + let r = max(resize(s.space), newLen) + result = cast[PGenericSeq](newSeq(typ, r)) + copyMem(result +! GenericSeqSize, s +! GenericSeqSize, s.len * elemSize) + # since we steal the content from 's', it's crucial to set s's len to 0. + s.len = 0 + elif newLen < s.len: + result = s + # we need to decref here, otherwise the GC leaks! + when not defined(boehmGC) and not defined(nogc) and + not defined(gcMarkAndSweep) and not defined(gogc) and + not defined(gcRegions): + if ntfNoRefs notin typ.base.flags: + for i in newLen..result.len-1: + forAllChildrenAux(cast[pointer](cast[ByteAddress](result) +% + GenericSeqSize +% (i*%elemSize)), + extGetCellType(result).base, waZctDecRef) + + # XXX: zeroing out the memory can still result in crashes if a wiped-out + # cell is aliased by another pointer (ie proc parameter or a let variable). + # This is a tough problem, because even if we don't zeroMem here, in the + # presence of user defined destructors, the user will expect the cell to be + # "destroyed" thus creating the same problem. We can destoy the cell in the + # finalizer of the sequence, but this makes destruction non-deterministic. + zeroMem(cast[pointer](cast[ByteAddress](result) +% GenericSeqSize +% + (newLen*%elemSize)), (result.len-%newLen) *% elemSize) + else: + result = s + result.len = newLen + else: + result = setLengthSeq(s, typ.base.size, newLen) diff --git a/tests/async/t6100.nim b/tests/async/t6100.nim deleted file mode 100644 index b4dc0f146..000000000 --- a/tests/async/t6100.nim +++ /dev/null @@ -1,15 +0,0 @@ -discard """ - file: "t6100.nim" - exitcode: 0 - output: "10000000" -""" -import asyncdispatch - -let done = newFuture[int]() -done.complete(1) - -proc asyncSum: Future[int] {.async.} = - for _ in 1..10_000_000: - result += await done - -echo waitFor asyncSum() \ No newline at end of file diff --git a/tests/async/t7985.nim b/tests/async/t7985.nim deleted file mode 100644 index 0365499d3..000000000 --- a/tests/async/t7985.nim +++ /dev/null @@ -1,19 +0,0 @@ -discard """ - file: "t7985.nim" - exitcode: 0 - output: "(value: 1)" -""" -import json, asyncdispatch - -proc getData(): Future[JsonNode] {.async.} = - result = %*{"value": 1} - -type - MyData = object - value: BiggestInt - -proc main() {.async.} = - let data = to(await(getData()), MyData) - echo data - -waitFor(main()) diff --git a/tests/async/tasync_misc.nim b/tests/async/tasync_misc.nim new file mode 100644 index 000000000..695dcd98a --- /dev/null +++ b/tests/async/tasync_misc.nim @@ -0,0 +1,47 @@ +discard """ + exitcode: 0 + output: "ok" +""" + +import json, asyncdispatch +block: #6100 + let done = newFuture[int]() + done.complete(1) + + proc asyncSum: Future[int] {.async.} = + for _ in 1..10_000_000: + result += await done + + let res = waitFor asyncSum() + doAssert(res == 10000000) + +block: #7985 + proc getData(): Future[JsonNode] {.async.} = + result = %*{"value": 1} + + type + MyData = object + value: BiggestInt + + proc main() {.async.} = + let data = to(await(getData()), MyData) + doAssert($data == "(value: 1)") + + waitFor(main()) + +block: #8399 + proc bar(): Future[string] {.async.} = discard + + proc foo(line: string) {.async.} = + var res = + case line[0] + of '+', '-': @[] + of '$': (let x = await bar(); @[""]) + else: + nil + + doAssert(res == @[""]) + + waitFor foo("$asd") + +echo "ok" diff --git a/tests/closure/tclosure3.nim b/tests/closure/tclosure3.nim index d5ffb5ab2..4de07bdb5 100644 --- a/tests/closure/tclosure3.nim +++ b/tests/closure/tclosure3.nim @@ -15,7 +15,7 @@ proc main = let val = s[i]() if val != $(i*i): echo "bug ", val - if getOccupiedMem() > 3000_000: quit("still a leak!") + if getOccupiedMem() > 5000_000: quit("still a leak!") echo "success" main() diff --git a/tests/errmsgs/t8434.nim b/tests/errmsgs/t8434.nim new file mode 100644 index 000000000..60fe2e2df --- /dev/null +++ b/tests/errmsgs/t8434.nim @@ -0,0 +1,16 @@ +discard """ + errormsg: "type mismatch: got <byte, int literal(0)>" + nimout: '''but expected one of: +proc fun0[T1: int | float | + object | array | seq](a1: T1; a2: int) + first type mismatch at position: 1 + required type: T1: int or float or object or array or seq + but expression 'byte(1)' is of type: byte + +expression: fun0(byte(1), 0) +''' +""" + +proc fun0[T1:int|float|object|array|seq](a1:T1, a2:int)=discard + +fun0(byte(1), 0) diff --git a/tests/fragmentation/tfragment_alloc.nim b/tests/fragmentation/tfragment_alloc.nim index 5a44b7434..80341d2dc 100644 --- a/tests/fragmentation/tfragment_alloc.nim +++ b/tests/fragmentation/tfragment_alloc.nim @@ -16,9 +16,12 @@ proc main = dealloc p # c_fprintf(stdout, "iteration: %ld size: %ld\n", i, size) when defined(cpu64): - # bug #7120 - var x = alloc(((1 shl 29) - 4) * 8) - dealloc x + # see https://github.com/nim-lang/Nim/issues/8509 + # this often made appveyor (on windows) fail with out of memory + when defined(posix): + # bug #7120 + var x = alloc(((1 shl 29) - 4) * 8) + dealloc x main() diff --git a/tests/gc/growobjcrash.nim b/tests/gc/growobjcrash.nim index a16468c7e..07f92b8f4 100644 --- a/tests/gc/growobjcrash.nim +++ b/tests/gc/growobjcrash.nim @@ -14,7 +14,7 @@ proc handleRequest(query: string): StringTableRef = let x = foo result = x() -const Limit = when compileOption("gc", "markAndSweep"): 5*1024*1024 else: 700_000 +const Limit = 5*1024*1024 proc main = var counter = 0 diff --git a/tests/generics/t8403.nim b/tests/generics/t8403.nim new file mode 100644 index 000000000..47ce9c452 --- /dev/null +++ b/tests/generics/t8403.nim @@ -0,0 +1,11 @@ +discard """ + output: "6.0" +""" + +proc sum*[T](s: seq[T], R: typedesc): R = + var sum: R = 0 + for x in s: + sum += R(x) + return sum + +echo @[1, 2, 3].sum(float) diff --git a/tests/generics/t8439.nim b/tests/generics/t8439.nim new file mode 100644 index 000000000..69bd7cfcb --- /dev/null +++ b/tests/generics/t8439.nim @@ -0,0 +1,10 @@ +discard """ + output: "1" +""" + +type + Cardinal = enum + north, east, south, west + +proc foo[cardinal: static[Cardinal]](): int = 1 +echo(foo[north]()) diff --git a/tests/iter/t338.nim b/tests/iter/t338.nim new file mode 100644 index 000000000..dbced8b60 --- /dev/null +++ b/tests/iter/t338.nim @@ -0,0 +1,20 @@ +discard """ + output: '''0 +1 +2 +3 +4 +''' +""" + +proc moo(): iterator (): int = + iterator fooGen: int {.closure.} = + while true: + yield result + result.inc + return fooGen + +var foo = moo() + +for i in 0 .. 4: + echo foo() diff --git a/tests/macros/tbindsym.nim b/tests/macros/tbindsym.nim index 6289d3eb2..2abcd98ce 100644 --- a/tests/macros/tbindsym.nim +++ b/tests/macros/tbindsym.nim @@ -1,4 +1,9 @@ discard """ + msg: '''initApple +deinitApple +Coral +enum + redCoral, blackCoral''' output: '''TFoo TBar''' """ @@ -23,3 +28,40 @@ macro test: untyped = bindSym("TBar")) test() + +# issue 7827, bindSym power up +{.experimental: "dynamicBindSym".} +type + Apple = ref object + name: string + color: int + weight: int + +proc initApple(name: string): Apple = + discard + +proc deinitApple(x: Apple) = + discard + +macro wrapObject(obj: typed, n: varargs[untyped]): untyped = + let m = n[0] + for x in m: + var z = bindSym x + echo z.repr + +wrapObject(Apple): + initApple + deinitApple + +type + Coral = enum + redCoral + blackCoral + +macro mixer(): untyped = + let m = "Co" & "ral" + let x = bindSym(m) + echo x.repr + echo getType(x).repr + +mixer() diff --git a/tests/macros/tmacrostmt.nim b/tests/macros/tmacrostmt.nim index a6e1e66dd..9dbfbce43 100644 --- a/tests/macros/tmacrostmt.nim +++ b/tests/macros/tmacrostmt.nim @@ -43,4 +43,35 @@ macro repr_and_parse(fn: typed): typed = echo fn_impl.repr result = parseStmt(fn_impl.repr) -repr_and_parse(f) \ No newline at end of file +repr_and_parse(f) + + +#------------------------------------ +# bugs #8343 and #8344 +proc one_if_proc(x, y : int): int = + if x < y: result = x + else: result = y + +proc test_block(x, y : int): int = + block label: + result = x + result = y + +#------------------------------------ +# bugs #8348 + +template `>`(x, y: untyped): untyped = + ## "is greater" operator. This is the same as ``y < x``. + y < x + +proc test_cond_stmtlist(x, y: int): int = + result = x + if x > y: + result = x + + +repr_and_parse(one_if_proc) +repr_and_parse(test_block) +repr_and_parse(test_cond_stmtlist) + + diff --git a/tests/misc/tsizeof.nim b/tests/misc/tsizeof.nim index 4afd48472..0597535f9 100644 --- a/tests/misc/tsizeof.nim +++ b/tests/misc/tsizeof.nim @@ -1,5 +1,7 @@ -# Test the sizeof proc - +discard """ + file: "tsize.nim" + output: "40 3 12 32" +""" type TMyRecord {.final.} = object x, y: int @@ -7,4 +9,20 @@ type r: float s: string + TMyEnum = enum + tmOne, tmTwo, tmThree, tmFour + + TMyArray1 = array[3, uint8] + TMyArray2 = array[1..3, int32] + TMyArray3 = array[TMyEnum, float64] + +const + mysize1 = sizeof(TMyArray1) + mysize2 = sizeof(TMyArray2) + mysize3 = sizeof(TMyArray3) + write(stdout, sizeof(TMyRecord)) +echo ' ', mysize1, ' ', mysize2, ' ',mysize3 + + + diff --git a/tests/pragmas/tcustom_pragma.nim b/tests/pragmas/tcustom_pragma.nim index a5f86b54d..d7b199a22 100644 --- a/tests/pragmas/tcustom_pragma.nim +++ b/tests/pragmas/tcustom_pragma.nim @@ -154,3 +154,23 @@ block: let a: proc(x: int) {.defaultValue(5).} = nil static: doAssert hasCustomPragma(a.type, defaultValue) + +# bug #8371 +template thingy {.pragma.} + +type + Cardinal = enum + north, east, south, west + Something = object + a: float32 + case cardinal: Cardinal + of north: + b {.thingy.}: int + of east: + c: int + of south: discard + else: discard + +var foo: Something +foo.cardinal = north +doAssert foo.b.hasCustomPragma(thingy) == true diff --git a/tests/sets/tsetpop.nim b/tests/sets/tsetpop.nim new file mode 100644 index 000000000..c37bda57d --- /dev/null +++ b/tests/sets/tsetpop.nim @@ -0,0 +1,22 @@ +discard """ + targets: "c c++ js" + output: '''1000 +0 +set is empty +''' +""" + +import sets + +var a = initSet[int]() +for i in 1..1000: + a.incl(i) +echo len(a) +for i in 1..1000: + discard a.pop() +echo len(a) + +try: + echo a.pop() +except KeyError as e: + echo e.msg \ No newline at end of file diff --git a/tests/sets/tsets.nim b/tests/sets/tsets.nim index 96d5debc7..13a5f54e6 100644 --- a/tests/sets/tsets.nim +++ b/tests/sets/tsets.nim @@ -205,4 +205,12 @@ echo warnUninit in gNotes # 7555 doAssert {-1.int8, -2, -2}.card == 2 -doAssert {1, 2, 2, 3..5, 4..6}.card == 6 \ No newline at end of file +doAssert {1, 2, 2, 3..5, 4..6}.card == 6 + +type Foo = enum + Foo1 = 0 + Foo2 = 1 + Foo3 = 3 + +let x = { Foo1, Foo2 } +# bug #8425 diff --git a/tests/stdlib/tencoding.nim b/tests/stdlib/tencoding.nim new file mode 100644 index 000000000..d6ff7ab32 --- /dev/null +++ b/tests/stdlib/tencoding.nim @@ -0,0 +1,21 @@ +discard """ + output: '''OK''' +""" + +#bug #8468 + +import encodings, strutils + +when defined(windows): + var utf16to8 = open(destEncoding = "utf-16", srcEncoding = "utf-8") + var s = "some string" + var c = utf16to8.convert(s) + + var z = newStringOfCap(s.len * 2) + for x in s: + z.add x + z.add chr(0) + + doAssert z == c + +echo "OK" diff --git a/tests/stdlib/tjsonmacro.nim b/tests/stdlib/tjsonmacro.nim index f13d2e5cb..c0b4d5f78 100644 --- a/tests/stdlib/tjsonmacro.nim +++ b/tests/stdlib/tjsonmacro.nim @@ -337,7 +337,7 @@ when isMainModule: n2: Option[int] n3: Option[string] n4: Option[bool] - + var j0 = parseJson("""{"n1": 1, "n2": null, "n3": null, "n4": null}""") let j0Deser = j0.to(Obj) doAssert j0Deser.n1 == 1 @@ -411,10 +411,109 @@ when isMainModule: doAssert dataDeser.a == 1 doAssert dataDeser.f == 6 doAssert dataDeser.i == 9.9'f32 - + # deserialize directly into a table block: let s = """{"a": 1, "b": 2}""" let t = parseJson(s).to(Table[string, int]) doAssert t["a"] == 1 - doAssert t["b"] == 2 \ No newline at end of file + doAssert t["b"] == 2 + + block: + # bug #8037 + type + Apple = distinct string + String = distinct Apple + Email = distinct string + MyList = distinct seq[int] + MyYear = distinct Option[int] + MyTable = distinct Table[string, int] + MyArr = distinct array[3, float] + MyRef = ref object + name: string + MyObj = object + color: int + MyDistRef = distinct MyRef + MyDistObj = distinct MyObj + Toot = object + name*: String + email*: Email + list: MyList + year: MyYear + dict: MyTable + arr: MyArr + person: MyDistRef + distfruit: MyDistObj + dog: MyRef + fruit: MyObj + emails: seq[String] + + var tJson = parseJson(""" + { + "name":"Bongo", + "email":"bongo@bingo.com", + "list": [11,7,15], + "year": 1975, + "dict": {"a": 1, "b": 2}, + "arr": [1.0, 2.0, 7.0], + "person": {"name": "boney"}, + "dog": {"name": "honey"}, + "fruit": {"color": 10}, + "distfruit": {"color": 11}, + "emails": ["abc", "123"] + } + """) + + var t = to(tJson, Toot) + doAssert string(t.name) == "Bongo" + doAssert string(t.email) == "bongo@bingo.com" + doAssert seq[int](t.list) == @[11,7,15] + doAssert Option[int](t.year).get() == 1975 + doAssert Table[string,int](t.dict)["a"] == 1 + doAssert Table[string,int](t.dict)["b"] == 2 + doAssert array[3, float](t.arr) == [1.0,2.0,7.0] + doAssert MyRef(t.person).name == "boney" + doAssert MyObj(t.distFruit).color == 11 + doAssert t.dog.name == "honey" + doAssert t.fruit.color == 10 + doAssert seq[string](t.emails) == @["abc", "123"] + + block test_table: + var y = parseJson("""{"a": 1, "b": 2, "c": 3}""") + var u = y.to(MyTable) + var v = y.to(Table[string, int]) + doAssert Table[string, int](u)["a"] == 1 + doAssert Table[string, int](u)["b"] == 2 + doAssert Table[string, int](u)["c"] == 3 + doAssert v["a"] == 1 + + block primitive_string: + const kApple = "apple" + var u = newJString(kApple) + var v = u.to(Email) + var w = u.to(Apple) + var x = u.to(String) + doAssert string(v) == kApple + doAssert string(w) == kApple + doAssert string(x) == kApple + + block test_option: + var u = newJInt(1137) + var v = u.to(MyYear) + var w = u.to(Option[int]) + doAssert Option[int](v).get() == 1137 + doAssert w.get() == 1137 + + block test_object: + var u = parseJson("""{"color": 987}""") + var v = u.to(MyObj) + var w = u.to(MyDistObj) + doAssert v.color == 987 + doAssert MyObj(w).color == 987 + + block test_ref_object: + var u = parseJson("""{"name": "smith"}""") + var v = u.to(MyRef) + var w = u.to(MyDistRef) + doAssert v.name == "smith" + doAssert MyRef(w).name == "smith" diff --git a/tests/stdlib/tos.nim b/tests/stdlib/tos.nim index a376c66d9..10f439dfa 100644 --- a/tests/stdlib/tos.nim +++ b/tests/stdlib/tos.nim @@ -145,7 +145,7 @@ else: echo getLastModificationTime("a") == tm removeFile("a") -when defined(Linux) or defined(macosx): +when defined(posix): block normalizedPath: block relative: diff --git a/tests/system/t7894.nim b/tests/system/t7894.nim new file mode 100644 index 000000000..2808e5020 --- /dev/null +++ b/tests/system/t7894.nim @@ -0,0 +1,18 @@ +discard """ +""" + +import os + +const size = 250000000 +var saved = newSeq[seq[int8]]() + +for i in 0..22: + # one of these is 0.25GB. + #echo i + var x = newSeq[int8](size) + sleep(10) + saved.add(x) + +for x in saved: + #echo x.len + doAssert x.len == size diff --git a/tests/system/talloc.nim b/tests/system/talloc.nim index 18396041d..bf2cd97a8 100644 --- a/tests/system/talloc.nim +++ b/tests/system/talloc.nim @@ -1,54 +1,57 @@ +discard """ +""" + var x: ptr int x = cast[ptr int](alloc(7)) -assert x != nil +doAssert x != nil x = cast[ptr int](x.realloc(2)) -assert x != nil +doAssert x != nil x.dealloc() x = createU(int, 3) -assert x != nil +doAssert x != nil x.dealloc() x = create(int, 4) -assert cast[ptr array[4, int]](x)[0] == 0 -assert cast[ptr array[4, int]](x)[1] == 0 -assert cast[ptr array[4, int]](x)[2] == 0 -assert cast[ptr array[4, int]](x)[3] == 0 +doAssert cast[ptr array[4, int]](x)[0] == 0 +doAssert cast[ptr array[4, int]](x)[1] == 0 +doAssert cast[ptr array[4, int]](x)[2] == 0 +doAssert cast[ptr array[4, int]](x)[3] == 0 x = x.resize(4) -assert x != nil +doAssert x != nil x.dealloc() x = cast[ptr int](allocShared(100)) -assert x != nil +doAssert x != nil deallocShared(x) x = createSharedU(int, 3) -assert x != nil +doAssert x != nil x.deallocShared() x = createShared(int, 3) -assert x != nil -assert cast[ptr array[3, int]](x)[0] == 0 -assert cast[ptr array[3, int]](x)[1] == 0 -assert cast[ptr array[3, int]](x)[2] == 0 +doAssert x != nil +doAssert cast[ptr array[3, int]](x)[0] == 0 +doAssert cast[ptr array[3, int]](x)[1] == 0 +doAssert cast[ptr array[3, int]](x)[2] == 0 -assert x != nil +doAssert x != nil x = cast[ptr int](x.resizeShared(2)) -assert x != nil +doAssert x != nil x.deallocShared() x = create(int, 10) -assert x != nil +doAssert x != nil x = x.resize(12) -assert x != nil +doAssert x != nil x.dealloc() x = createShared(int, 1) -assert x != nil +doAssert x != nil x = x.resizeShared(1) -assert x != nil +doAssert x != nil x.deallocShared() x = cast[ptr int](alloc0(125 shl 23)) diff --git a/tests/system/talloc2.nim b/tests/system/talloc2.nim index c8cab78a1..0757c0724 100644 --- a/tests/system/talloc2.nim +++ b/tests/system/talloc2.nim @@ -1,3 +1,6 @@ +discard """ +""" + const nmax = 2*1024*1024*1024 diff --git a/tests/system/tdeepcopy.nim b/tests/system/tdeepcopy.nim index f7a6e87fa..383d2e8d1 100644 --- a/tests/system/tdeepcopy.nim +++ b/tests/system/tdeepcopy.nim @@ -65,7 +65,7 @@ proc main() = for val in table.values(): if myObj2.isNil: myObj2 = val - assert(myObj == myObj2) # passes + doAssert(myObj == myObj2) # passes var tableCopy: ListTableRef[int, SomeObj] deepCopy(tableCopy, table) @@ -80,7 +80,7 @@ proc main() = #echo cast[int](myObjCopy) #echo cast[int](myObjCopy2) - assert(myObjCopy == myObjCopy2) # fails + doAssert(myObjCopy == myObjCopy2) # fails type @@ -88,7 +88,7 @@ type counter, max: int data: array[0..99, (pointer, pointer)] -assert(sizeof(PtrTable) == 2*sizeof(int)+sizeof(pointer)*2*100) +doAssert(sizeof(PtrTable) == 2*sizeof(int)+sizeof(pointer)*2*100) main() echo "ok" diff --git a/tests/system/tio.nim b/tests/system/tio.nim index 3d4df806b..7e9e18950 100644 --- a/tests/system/tio.nim +++ b/tests/system/tio.nim @@ -1,3 +1,6 @@ +discard """ +""" + import unittest, osproc, streams, os, strformat const STRING_DATA = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet." @@ -36,7 +39,7 @@ proc verifyFileSize(sz: int64) = discard execProcess(&"dd if=/dev/zero of={fn} bs=1000000 count={size_in_mb}") doAssert os.getFileSize(fn) == sz # Verify OS filesize by string - + var f = open(fn) doAssert f.getFileSize() == sz # Verify file handle filesize f.close() diff --git a/tests/system/tparams.nim b/tests/system/tparams.nim index 1358212f2..dd5511b8f 100644 --- a/tests/system/tparams.nim +++ b/tests/system/tparams.nim @@ -1,3 +1,6 @@ +discard """ +""" + import os import osproc import parseopt2 @@ -7,12 +10,12 @@ let argv = commandLineParams() if argv == @[]: # this won't work with spaces - assert execShellCmd(getAppFilename() & " \"foo bar\" --aa:bar=a --a=c:d --ab -c --a[baz]:doo") == 0 + doAssert execShellCmd(getAppFilename() & " \"foo bar\" --aa:bar=a --a=c:d --ab -c --a[baz]:doo") == 0 else: let f = toSeq(getopt()) echo f.repr - assert f[0].kind == cmdArgument and f[0].key == "foo bar" and f[0].val == "" - assert f[1].kind == cmdLongOption and f[1].key == "aa" and f[1].val == "bar=a" - assert f[2].kind == cmdLongOption and f[2].key == "a=c" and f[2].val == "d" - assert f[3].kind == cmdLongOption and f[3].key == "ab" and f[3].val == "" - assert f[4].kind == cmdShortOption and f[4].key == "c" and f[4].val == "" + doAssert f[0].kind == cmdArgument and f[0].key == "foo bar" and f[0].val == "" + doAssert f[1].kind == cmdLongOption and f[1].key == "aa" and f[1].val == "bar=a" + doAssert f[2].kind == cmdLongOption and f[2].key == "a=c" and f[2].val == "d" + doAssert f[3].kind == cmdLongOption and f[3].key == "ab" and f[3].val == "" + doAssert f[4].kind == cmdShortOption and f[4].key == "c" and f[4].val == "" diff --git a/tests/vm/tref.nim b/tests/vm/tref.nim index 27b7bf313..f5cd23da5 100644 --- a/tests/vm/tref.nim +++ b/tests/vm/tref.nim @@ -54,4 +54,9 @@ static: new(s) var ss = s s[] = 1 - doAssert ss[] == 1 \ No newline at end of file + doAssert ss[] == 1 + +static: # bug #8402 + type R = ref object + var empty: R + let otherEmpty = empty \ No newline at end of file diff --git a/tools/dochack/dochack.nim b/tools/dochack/dochack.nim index 79a0e7482..8cc27b6eb 100644 --- a/tools/dochack/dochack.nim +++ b/tools/dochack/dochack.nim @@ -1,6 +1,5 @@ - - import karax +import fuzzysearch proc findNodeWith(x: Element; tag, content: cstring): Element = if x.nodeName == tag and x.textContent == content: @@ -88,11 +87,11 @@ proc toHtml(x: TocEntry; isRoot=false): Element = if ul.len != 0: result.add ul if result.len == 0: result = nil -proc containsWord(a, b: cstring): bool {.asmNoStackFrame.} = - {.emit: """ - var escaped = `b`.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); - return new RegExp("\\b" + escaped + "\\b").test(`a`); - """.} +#proc containsWord(a, b: cstring): bool {.asmNoStackFrame.} = + #{.emit: """ + #var escaped = `b`.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); + #return new RegExp("\\b" + escaped + "\\b").test(`a`); + #""".} proc isWhitespace(text: cstring): bool {.asmNoStackFrame.} = {.emit: """ @@ -252,24 +251,29 @@ proc dosearch(value: cstring): Element = `stuff` = doc.documentElement; """.} - db = stuff.getElementsByClass"reference external" + db = stuff.getElementsByClass"reference" contents = @[] for ahref in db: - contents.add ahref.textContent.normalize + contents.add ahref.getAttribute("data-doc-search-tag") let ul = tree("UL") result = tree("DIV") result.setClass"search_results" var matches: seq[(Element, int)] = @[] - let key = value.normalize for i in 0..<db.len: let c = contents[i] - if c.containsWord(key): - matches.add((db[i], -(30_000 - c.len))) - elif c.contains(key): - matches.add((db[i], c.len)) + if c == "Examples" or c == "PEG construction": + # Some manual exclusions. + # Ideally these should be fixed in the index to be more + # descriptive of what they are. + continue + let (score, matched) = fuzzymatch(value, c) + if matched: + matches.add((db[i], score)) + matches.sort do (a, b: auto) -> int: - a[1] - b[1] - for i in 0..min(<matches.len, 19): + b[1] - a[1] + for i in 0 ..< min(matches.len, 19): + matches[i][0].innerHTML = matches[i][0].getAttribute("data-doc-search-tag") ul.add(tree("LI", matches[i][0])) if ul.len == 0: result.add tree("B", text"no search results") diff --git a/tools/dochack/fuzzysearch.nim b/tools/dochack/fuzzysearch.nim new file mode 100644 index 000000000..e6b1ea3cd --- /dev/null +++ b/tools/dochack/fuzzysearch.nim @@ -0,0 +1,139 @@ +# A Fuzzy Match implementation inspired by the sublime text fuzzy match algorithm +# as described here: https://blog.forrestthewoods.com/reverse-engineering-sublime-text-s-fuzzy-match-4cffeed33fdb +# Heavily modified to provide more subjectively useful results +# for on the Nim manual. +# +import strutils +import math +import macros + + +const + MaxUnmatchedLeadingChar = 3 + ## Maximum number of times the penalty for unmatched leading chars is applied. + + HeadingScaleFactor = 0.5 + ## The score from before the colon Char is multiplied by this. + ## This is to weight function signatures and descriptions over module titles. + + +type + ScoreCard = enum + StartMatch = -100 ## Start matching. + LeadingCharDiff = -3 ## An unmatched, leading character was found. + CharDiff = -1 ## An unmatched character was found. + CharMatch = 0 ## A matched character was found. + ConsecutiveMatch = 5 ## A consecutive match was found. + LeadingCharMatch = 10 ## The character matches the begining of the + ## string or the first character of a word + ## or camel case boundry. + WordBoundryMatch = 20 ## The last ConsecutiveCharMatch that + ## immediately precedes the end of the string, + ## end of the pattern, or a LeadingCharMatch. + + +proc fuzzyMatch*(pattern, str: cstring) : tuple[score: int, matched: bool] = + var + scoreState = StartMatch + headerMatched = false + unmatchedLeadingCharCount = 0 + consecutiveMatchCount = 0 + strIndex = 0 + patIndex = 0 + score = 0 + + template transition(nextState) = + scoreState = nextState + score += ord(scoreState) + + while (strIndex < str.len) and (patIndex < pattern.len): + var + patternChar = pattern[patIndex].toLowerAscii + strChar = str[strIndex].toLowerAscii + + # Ignore certain characters + if patternChar in {'_', ' ', '.'}: + patIndex += 1 + continue + if strChar in {'_', ' ', '.'}: + strIndex += 1 + continue + + # Since this algorithm will be used to search against Nim documentation, + # the below logic prioritizes headers. + if not headerMatched and strChar == ':': + headerMatched = true + scoreState = StartMatch + score = toInt(floor(HeadingScaleFactor * toFloat(score))) + patIndex = 0 + strIndex += 1 + continue + + if strChar == patternChar: + case scoreState + of StartMatch, WordBoundryMatch: + scoreState = LeadingCharMatch + + of CharMatch: + transition(ConsecutiveMatch) + + of LeadingCharMatch, ConsecutiveMatch: + consecutiveMatchCount += 1 + scoreState = ConsecutiveMatch + score += ord(ConsecutiveMatch) * consecutiveMatchCount + + if scoreState == LeadingCharMatch: + score += ord(LeadingCharMatch) + + var onBoundary = (patIndex == high(pattern)) + if not onBoundary: + let + nextPatternChar = toLowerAscii(pattern[patIndex + 1]) + nextStrChar = toLowerAscii(str[strIndex + 1]) + + onBoundary = ( + nextStrChar notin {'a'..'z'} and + nextStrChar != nextPatternChar + ) + + if onBoundary: + transition(WordBoundryMatch) + + of CharDiff, LeadingCharDiff: + var isLeadingChar = ( + str[strIndex - 1] notin Letters or + str[strIndex - 1] in {'a'..'z'} and + str[strIndex] in {'A'..'Z'} + ) + + if isLeadingChar: + scoreState = LeadingCharMatch + #a non alpha or a camel case transition counts as a leading char. + # Transition the state, but don't give the bonus yet; wait until we verify a consecutive match. + else: + transition(CharMatch) + patIndex += 1 + + else: + case scoreState + of StartMatch: + transition(LeadingCharDiff) + + of ConsecutiveMatch: + transition(CharDiff) + consecutiveMatchCount = 0 + + of LeadingCharDiff: + if unmatchedLeadingCharCount < MaxUnmatchedLeadingChar: + transition(LeadingCharDiff) + unmatchedLeadingCharCount += 1 + + else: + transition(CharDiff) + + strIndex += 1 + + result = ( + score: max(0, score), + matched: (score > 0), + ) diff --git a/tools/nimweb.nim b/tools/nimweb.nim index 6e1d9d359..e74b081ea 100644 --- a/tools/nimweb.nim +++ b/tools/nimweb.nim @@ -13,16 +13,18 @@ import from xmltree import escape -const gitRepo = "https://github.com/nim-lang/Nim" - type TKeyValPair = tuple[key, id, val: string] TConfigData = object of RootObj tabs, links: seq[TKeyValPair] doc, srcdoc, srcdoc2, webdoc, pdf: seq[string] - authors, projectName, projectTitle, logo, infile, outdir, ticker: string + authors, projectName, projectTitle, logo, infile, ticker: string vars: StringTableRef + nimCompiler: string nimArgs: string + gitURL: string + docHTMLOutput: string + webUploadOutput: string quotations: Table[string, tuple[quote, author: string]] numProcessors: int # Set by parallelBuild:n, only works for values > 0. gaId: string # google analytics ID, nil means analytics are disabled @@ -51,8 +53,10 @@ proc initConfigData(c: var TConfigData) = c.webdoc = @[] c.pdf = @[] c.infile = "" - c.outdir = "" c.nimArgs = "--hint[Conf]:off --hint[Path]:off --hint[Processing]:off -d:boot " + c.gitURL = "https://github.com/nim-lang/Nim" + c.docHTMLOutput = "doc/html" + c.webUploadOutput = "web/upload" c.authors = "" c.projectTitle = "" c.projectName = "" @@ -79,14 +83,19 @@ const Usage: nimweb [options] ini-file[.ini] [compile_options] Options: - -o, --output:dir set the output directory (default: same as ini-file) - --var:name=value set the value of a variable -h, --help shows this help -v, --version shows the version + -o, --output overrides output directory instead of default + web/upload and doc/html + --nimCompiler overrides nim compiler; default = bin/nim + --var:name=value set the value of a variable --website only build the website, not the full documentation --pdf build the PDF version of the documentation --json2 build JSON of the documentation --onlyDocs build only the documentation + --git.url override base url in generated doc links + --git.commit override commit/branch in generated doc links 'source' + --git.devel override devel branch in generated doc links 'edit' Compile_options: will be passed to the Nim compiler """ @@ -145,7 +154,11 @@ proc parseCmdLine(c: var TConfigData) = of "version", "v": stdout.write(version & "\n") quit(0) - of "o", "output": c.outdir = val + of "output", "o": + c.webUploadOutput = val + c.docHTMLOutput = val / "docs" + of "nimcompiler": + c.nimCompiler = val of "parallelbuild": try: let num = parseInt(val) @@ -163,8 +176,14 @@ proc parseCmdLine(c: var TConfigData) = of "googleanalytics": c.gaId = val c.nimArgs.add("--doc.googleAnalytics:" & val & " ") + of "git.url": + c.gitURL = val + of "git.commit": + c.nimArgs.add("--git.commit:" & val & " ") + of "git.devel": + c.nimArgs.add("--git.devel:" & val & " ") else: - echo("Invalid argument $1" % [key]) + echo("Invalid argument '$1'" % [key]) quit(usage) of cmdEnd: break if c.infile.len == 0: quit(usage) @@ -244,15 +263,14 @@ proc parseIniFile(c: var TConfigData) = close(p) if c.projectName.len == 0: c.projectName = changeFileExt(extractFilename(c.infile), "") - if c.outdir.len == 0: - c.outdir = splitFile(c.infile).dir # ------------------- main ---------------------------------------------------- proc exe(f: string): string = return addFileExt(f, ExeExt) -proc findNim(): string = +proc findNim(c: TConfigData): string = + if c.nimCompiler.len > 0: return c.nimCompiler var nim = "nim".exe result = "bin" / nim if existsFile(result): return @@ -289,9 +307,9 @@ proc buildDocSamples(c: var TConfigData, destPath: string) = ## it didn't make much sense to integrate into the existing generic ## documentation builders. const src = "doc"/"docgen_sample.nim" - exec(findNim() & " doc $# -o:$# $#" % + exec(findNim(c) & " doc $# -o:$# $#" % [c.nimArgs, destPath / "docgen_sample.html", src]) - exec(findNim() & " doc2 $# -o:$# $#" % + exec(findNim(c) & " doc2 $# -o:$# $#" % [c.nimArgs, destPath / "docgen_sample2.html", src]) proc pathPart(d: string): string = splitFile(d).dir.replace('\\', '/') @@ -302,23 +320,23 @@ proc buildDoc(c: var TConfigData, destPath: string) = commands = newSeq[string](len(c.doc) + len(c.srcdoc) + len(c.srcdoc2)) i = 0 for d in items(c.doc): - commands[i] = findNim() & " rst2html $# --git.url:$# -o:$# --index:on $#" % - [c.nimArgs, gitRepo, + commands[i] = findNim(c) & " rst2html $# --git.url:$# -o:$# --index:on $#" % + [c.nimArgs, c.gitURL, destPath / changeFileExt(splitFile(d).name, "html"), d] i.inc for d in items(c.srcdoc): - commands[i] = findNim() & " doc0 $# --git.url:$# -o:$# --index:on $#" % - [c.nimArgs, gitRepo, + commands[i] = findNim(c) & " doc0 $# --git.url:$# -o:$# --index:on $#" % + [c.nimArgs, c.gitURL, destPath / changeFileExt(splitFile(d).name, "html"), d] i.inc for d in items(c.srcdoc2): - commands[i] = findNim() & " doc2 $# --git.url:$# -o:$# --index:on $#" % - [c.nimArgs, gitRepo, + commands[i] = findNim(c) & " doc2 $# --git.url:$# -o:$# --index:on $#" % + [c.nimArgs, c.gitURL, destPath / changeFileExt(splitFile(d).name, "html"), d] i.inc mexec(commands, c.numProcessors) - exec(findNim() & " buildIndex -o:$1/theindex.html $1" % [destPath]) + exec(findNim(c) & " buildIndex -o:$1/theindex.html $1" % [destPath]) proc buildPdfDoc(c: var TConfigData, destPath: string) = createDir(destPath) @@ -327,7 +345,7 @@ proc buildPdfDoc(c: var TConfigData, destPath: string) = else: const pdflatexcmd = "pdflatex -interaction=nonstopmode " for d in items(c.pdf): - exec(findNim() & " rst2tex $# $#" % [c.nimArgs, d]) + exec(findNim(c) & " rst2tex $# $#" % [c.nimArgs, d]) # call LaTeX twice to get cross references right: exec(pdflatexcmd & changeFileExt(d, "tex")) exec(pdflatexcmd & changeFileExt(d, "tex")) @@ -347,8 +365,8 @@ proc buildAddDoc(c: var TConfigData, destPath: string) = # build additional documentation (without the index): var commands = newSeq[string](c.webdoc.len) for i, doc in pairs(c.webdoc): - commands[i] = findNim() & " doc2 $# --git.url:$# -o:$# $#" % - [c.nimArgs, gitRepo, + commands[i] = findNim(c) & " doc2 $# --git.url:$# -o:$# $#" % + [c.nimArgs, c.gitURL, destPath / changeFileExt(splitFile(doc).name, "html"), doc] mexec(commands, c.numProcessors) @@ -431,9 +449,9 @@ proc buildNewsRss(c: var TConfigData, destPath: string) = generateRss(destFilename, parseNewsTitles(srcFilename)) -proc buildJS(destPath: string) = - exec(findNim() & " js -d:release --out:$1 web/nimblepkglist.nim" % - [destPath / "nimblepkglist.js"]) +proc buildJS(c: TConfigData) = + exec(findNim(c) & " js -d:release --out:$1 web/nimblepkglist.nim" % + [c.webUploadOutput / "nimblepkglist.js"]) proc readSponsors(sponsorsFile: string): seq[Sponsor] = result = @[] @@ -464,7 +482,7 @@ const cmdRst2Html = " rst2html --compileonly $1 -o:web/$2.temp web/$2.rst" proc buildPage(c: var TConfigData, file, title, rss: string, assetDir = "") = - exec(findNim() & cmdRst2Html % [c.nimArgs, file]) + exec(findNim(c) & cmdRst2Html % [c.nimArgs, file]) var temp = "web" / changeFileExt(file, "temp") var content: string try: @@ -472,7 +490,7 @@ proc buildPage(c: var TConfigData, file, title, rss: string, assetDir = "") = except IOError: quit("[Error] cannot open: " & temp) var f: File - var outfile = "web/upload/$#.html" % file + var outfile = c.webUploadOutput / "$#.html" % file if not existsDir(outfile.splitFile.dir): createDir(outfile.splitFile.dir) if open(f, outfile, fmWrite): @@ -502,27 +520,25 @@ proc buildWebsite(c: var TConfigData) = let rss = if file in ["news", "index"]: extractFilename(rssUrl) else: "" if '.' in file: continue buildPage(c, file, if file == "question": "FAQ" else: file, rss) - copyDir("web/assets", "web/upload/assets") - buildNewsRss(c, "web/upload") - buildSponsors(c, "web/upload") - buildNews(c, "web/news", "web/upload/news") + copyDir("web/assets", c.webUploadOutput / "assets") + buildNewsRss(c, c.webUploadOutput) + buildSponsors(c, c.webUploadOutput) + buildNews(c, "web/news", c.webUploadOutput / "news") + +proc onlyDocs(c: var TConfigData) = + createDir(c.docHTMLOutput) + buildDocSamples(c, c.docHTMLOutput) + buildDoc(c, c.docHTMLOutput) proc main(c: var TConfigData) = buildWebsite(c) - buildJS("web/upload") - const docup = "web/upload/" & NimVersion + buildJS(c) + let docup = c.webUploadOutput / NimVersion createDir(docup) buildAddDoc(c, docup) buildDocSamples(c, docup) buildDoc(c, docup) - createDir("doc/html") - buildDocSamples(c, "doc/html") - buildDoc(c, "doc/html") - -proc onlyDocs(c: var TConfigData) = - createDir("doc/html") - buildDocSamples(c, "doc/html") - buildDoc(c, "doc/html") + onlyDocs(c) proc json2(c: var TConfigData) = const destPath = "web/json2" @@ -530,8 +546,8 @@ proc json2(c: var TConfigData) = var i = 0 for d in items(c.srcdoc2): createDir(destPath / splitFile(d).dir) - commands[i] = findNim() & " jsondoc2 $# --git.url:$# -o:$# --index:on $#" % - [c.nimArgs, gitRepo, + commands[i] = findNim(c) & " jsondoc2 $# --git.url:$# -o:$# --index:on $#" % + [c.nimArgs, c.gitURL, destPath / changeFileExt(d, "json"), d] i.inc |