make at least bootstrapping work

This commit is contained in:
Araq 2018-08-05 14:17:24 +02:00
commit 7ac6462cbd
81 changed files with 1314 additions and 425 deletions

3
.gitignore vendored
View file

@ -58,6 +58,7 @@ dist/
.*/ .*/
~* ~*
# testament cruft # testament cruft; TODO: generate these in a gitignore'd dir in the first place.
testresults/ testresults/
test.txt test.txt
/test.ini

View file

@ -22,7 +22,8 @@
become an error in the future. become an error in the future.
- The ``'c`` and ``'C'`` prefix for octal literals is now deprecated to - The ``'c`` and ``'C'`` prefix for octal literals is now deprecated to
bring the language in line with the standard library (e.g. ``parseOct``). 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 #### Breaking changes in the standard library
@ -59,6 +60,8 @@
1-based coordinates on POSIX for correct behaviour; the Windows behaviour 1-based coordinates on POSIX for correct behaviour; the Windows behaviour
was always correct). 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 #### 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) - Nintendo Switch was added as a new platform target. See [the compiler user guide](https://nim-lang.org/docs/nimc.html)
for more info. 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 ### Bugfixes

View file

@ -1085,7 +1085,7 @@ proc genStrAppend(p: BProc, e: PNode, d: var TLoc) =
# appendChar(s, 'z'); # appendChar(s, 'z');
# } # }
var var
a, dest: TLoc a, dest, call: TLoc
appends, lens: Rope appends, lens: Rope
assert(d.k == locNone) assert(d.k == locNone)
var L = 0 var L = 0
@ -1109,8 +1109,9 @@ proc genStrAppend(p: BProc, e: PNode, d: var TLoc) =
linefmt(p, cpsStmts, "#prepareAdd($1, $2$3);$n", linefmt(p, cpsStmts, "#prepareAdd($1, $2$3);$n",
addrLoc(p.config, dest), lens, rope(L)) addrLoc(p.config, dest), lens, rope(L))
else: else:
linefmt(p, cpsStmts, "$1 = #resizeString($1, $2$3);$n", initLoc(call, locCall, e, OnHeap)
rdLoc(dest), lens, rope(L)) call.r = ropecg(p.module, "#resizeString($1, $2$3)", [rdLoc(dest), lens, rope(L)])
genAssignment(p, dest, call, {})
add(p.s(cpsStmts), appends) add(p.s(cpsStmts), appends)
gcUsage(p.config, e) 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 = (typeof seq) incrSeq(&seq->Sup, sizeof(x));
# seq->data[seq->len-1] = x; # seq->data[seq->len-1] = x;
let seqAppendPattern = if not p.module.compileToCpp: let seqAppendPattern = if not p.module.compileToCpp:
"$1 = ($2) #incrSeqV3(&($1)->Sup, $3);$n" "($2) #incrSeqV3(&($1)->Sup, $3)"
else: else:
"$1 = ($2) #incrSeqV3($1, $3);$n" "($2) #incrSeqV3($1, $3)"
var a, b, dest, tmpL: TLoc var a, b, dest, tmpL, call: TLoc
initLocExpr(p, e.sons[1], a) initLocExpr(p, e.sons[1], a)
initLocExpr(p, e.sons[2], b) initLocExpr(p, e.sons[2], b)
let seqType = skipTypes(e.sons[1].typ, {tyVar}) let seqType = skipTypes(e.sons[1].typ, {tyVar})
lineCg(p, cpsStmts, seqAppendPattern, [ initLoc(call, locCall, e, OnHeap)
rdLoc(a), call.r = ropecg(p.module, seqAppendPattern, [rdLoc(a),
getTypeDesc(p.module, e.sons[1].typ), getTypeDesc(p.module, e.sons[1].typ),
genTypeInfo(p.module, seqType, e.info)]) 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: #if bt != b.t:
# echo "YES ", e.info, " new: ", typeToString(bt), " old: ", typeToString(b.t) # echo "YES ", e.info, " new: ", typeToString(bt), " old: ", typeToString(b.t)
initLoc(dest, locExpr, e.sons[2], OnHeap) 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: if p.config.selectedGc == gcDestructors:
genCall(p, e, d) genCall(p, e, d)
return return
var a, b: TLoc var a, b, call: TLoc
assert(d.k == locNone) assert(d.k == locNone)
var x = e.sons[1] var x = e.sons[1]
if x.kind in {nkAddr, nkHiddenAddr}: x = x[0] 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) initLocExpr(p, e.sons[2], b)
let t = skipTypes(e.sons[1].typ, {tyVar}) let t = skipTypes(e.sons[1].typ, {tyVar})
let setLenPattern = if not p.module.compileToCpp: let setLenPattern = if not p.module.compileToCpp:
"$1 = ($3) #setLengthSeqV2(&($1)->Sup, $4, $2);$n" "($3) #setLengthSeqV2(&($1)->Sup, $4, $2)"
else: 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), rdLoc(a), rdLoc(b), getTypeDesc(p.module, t),
genTypeInfo(p.module, t.skipTypes(abstractInst), e.info)]) genTypeInfo(p.module, t.skipTypes(abstractInst), e.info)])
genAssignment(p, a, call, {})
gcUsage(p.config, e) gcUsage(p.config, e)
proc genSetLengthStr(p: BProc, e: PNode, d: var TLoc) = proc genSetLengthStr(p: BProc, e: PNode, d: var TLoc) =
if p.config.selectedGc == gcDestructors: if p.config.selectedGc == gcDestructors:
binaryStmtAddr(p, e, d, "#setLengthStrV2($1, $2);$n") binaryStmtAddr(p, e, d, "#setLengthStrV2($1, $2);$n")
else: 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) gcUsage(p.config, e)
proc genSwap(p: BProc, e: PNode, d: var TLoc) = 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: if p.config.selectedGC == gcDestructors:
binaryStmtAddr(p, e, d, "#nimAddCharV1($1, $2);$n") binaryStmtAddr(p, e, d, "#nimAddCharV1($1, $2);$n")
else: 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 mAppendStrStr: genStrAppend(p, e, d)
of mAppendSeqElem: of mAppendSeqElem:
if p.config.selectedGc == gcDestructors: if p.config.selectedGc == gcDestructors:

View file

@ -588,7 +588,7 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
let branch = n[i] let branch = n[i]
case branch.kind case branch.kind
of nkOfBranch: of nkOfBranch:
branch[1] = ctx.convertExprBodyToAsgn(branch[1], tmp) branch[^1] = ctx.convertExprBodyToAsgn(branch[^1], tmp)
of nkElse: of nkElse:
branch[0] = ctx.convertExprBodyToAsgn(branch[0], tmp) branch[0] = ctx.convertExprBodyToAsgn(branch[0], tmp)
else: else:

View file

@ -73,3 +73,4 @@ proc initDefines*(symbols: StringTableRef) =
defineSymbol("nimNotNil") defineSymbol("nimNotNil")
defineSymbol("nimVmExportFixed") defineSymbol("nimVmExportFixed")
defineSymbol("nimNewRuntime") defineSymbol("nimNewRuntime")
defineSymbol("nimIncrSeqV3")

View file

@ -259,7 +259,7 @@ proc nodeToHighlightedHtml(d: PDoc; n: PNode; result: var Rope; renderFlags: TRe
of tkSpaces, tkInvalid: of tkSpaces, tkInvalid:
add(result, literal) add(result, literal)
of tkCurlyDotLe: 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 class="Other">{</span><span class="Other pragmadots">...</span><span class="Other">}</span>
</span> </span>
<span class="pragmawrap"> <span class="pragmawrap">
@ -517,12 +517,11 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) =
path = path[cwd.len+1 .. ^1].replace('\\', '/') path = path[cwd.len+1 .. ^1].replace('\\', '/')
let gitUrl = getConfigVar(d.conf, "git.url") let gitUrl = getConfigVar(d.conf, "git.url")
if gitUrl.len > 0: if gitUrl.len > 0:
var commit = getConfigVar(d.conf, "git.commit") let commit = getConfigVar(d.conf, "git.commit", "master")
if commit.len == 0: commit = "master" let develBranch = getConfigVar(d.conf, "git.devel", "devel")
dispA(d.conf, seeSrcRope, "$1", "", [ropeFormatNamedVars(d.conf, docItemSeeSrc, dispA(d.conf, seeSrcRope, "$1", "", [ropeFormatNamedVars(d.conf, docItemSeeSrc,
["path", "line", "url", "commit"], [rope path, ["path", "line", "url", "commit", "devel"], [rope path,
rope($n.info.line), rope gitUrl, rope($n.info.line), rope gitUrl, rope commit, rope develBranch])])
rope commit])])
add(d.section[k], ropeFormatNamedVars(d.conf, getConfigVar(d.conf, "doc.item"), add(d.section[k], ropeFormatNamedVars(d.conf, getConfigVar(d.conf, "doc.item"),
["name", "header", "desc", "itemID", "header_plain", "itemSym", ["name", "header", "desc", "itemID", "header_plain", "itemSym",

View file

@ -92,7 +92,7 @@ compiler nintendoSwitchGCC:
optSize: " -Os -ffast-math ", optSize: " -Os -ffast-math ",
compilerExe: "aarch64-none-elf-gcc", compilerExe: "aarch64-none-elf-gcc",
cppCompiler: "aarch64-none-elf-g++", 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", buildGui: " -mwindows",
buildDll: " -shared", buildDll: " -shared",
buildLib: "aarch64-none-elf-gcc-ar rcs $libfile $objfiles", 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: if optCompileOnly notin conf.globalOptions:
add(cmds, compileCmd) add(cmds, compileCmd)
let (_, name, _) = splitFile(it.cname) let (_, name, _) = splitFile(it.cname)
add(prettyCmds, "CC: " & name) add(prettyCmds, if hintCC in conf.notes: "CC: " & name else: "")
if optGenScript in conf.globalOptions: if optGenScript in conf.globalOptions:
add(script, compileCmd) add(script, compileCmd)
add(script, "\n") add(script, "\n")
@ -659,7 +659,7 @@ proc getLinkCmd(conf: ConfigRef; projectfile, objfiles: string): string =
libname = getCurrentDir() / libname libname = getCurrentDir() / libname
else: else:
libname = (libNameTmpl(conf) % splitFile(conf.projectName).name) libname = (libNameTmpl(conf) % splitFile(conf.projectName).name)
result = CC[conf.cCompiler].buildLib % ["libfile", libname, result = CC[conf.cCompiler].buildLib % ["libfile", quoteShell(libname),
"objfiles", objfiles] "objfiles", objfiles]
else: else:
var linkerExe = getConfigVar(conf, conf.cCompiler, ".linkerexe") 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 needsExeExt(conf): linkerExe = addFileExt(linkerExe, "exe")
if noAbsolutePaths(conf): result = linkerExe if noAbsolutePaths(conf): result = linkerExe
else: result = joinPath(conf.cCompilerpath, linkerExe) else: result = joinPath(conf.cCompilerpath, linkerExe)
let buildgui = if optGenGuiApp in conf.globalOptions: CC[conf.cCompiler].buildGui let buildgui = if optGenGuiApp in conf.globalOptions and conf.target.targetOS == osWindows:
else: "" CC[conf.cCompiler].buildGui
else:
""
var exefile, builddll: string var exefile, builddll: string
if optGenDynLib in conf.globalOptions: if optGenDynLib in conf.globalOptions:
exefile = platform.OS[conf.target.targetOS].dllFrmt % splitFile(projectfile).name exefile = platform.OS[conf.target.targetOS].dllFrmt % splitFile(projectfile).name
@ -771,7 +773,8 @@ proc callCCompiler*(conf: ConfigRef; projectfile: string) =
var prettyCmds: TStringSeq = @[] var prettyCmds: TStringSeq = @[]
let prettyCb = proc (idx: int) = let prettyCb = proc (idx: int) =
when declared(echo): when declared(echo):
echo prettyCmds[idx] let cmd = prettyCmds[idx]
if cmd != "": echo cmd
compileCFile(conf, conf.toCompile, script, cmds, prettyCmds) compileCFile(conf, conf.toCompile, script, cmds, prettyCmds)
if optCompileOnly notin conf.globalOptions: if optCompileOnly notin conf.globalOptions:
execCmdsInParallel(conf, cmds, prettyCb) execCmdsInParallel(conf, cmds, prettyCb)

View file

@ -222,7 +222,7 @@ proc interestingIterVar(s: PSym): bool {.inline.} =
# XXX optimization: Only lift the variable if it lives across # XXX optimization: Only lift the variable if it lives across
# yield/return boundaries! This can potentially speed up # yield/return boundaries! This can potentially speed up
# closure iterators quite a bit. # 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 = template isIterator*(owner: PSym): bool =
owner.kind == skIterator and owner.typ.callConv == ccClosure 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: of nkLambdaKinds, nkIteratorDef, nkFuncDef:
if n.typ != nil: if n.typ != nil:
detectCapturedVars(n[namePos], owner, c) 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: else:
for i in 0..<n.len: for i in 0..<n.len:
detectCapturedVars(n[i], owner, c) detectCapturedVars(n[i], owner, c)
@ -687,6 +691,13 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass;
if n.len == 2: if n.len == 2:
n.sons[1] = liftCapturedVars(n[1], owner, d, c) n.sons[1] = liftCapturedVars(n[1], owner, d, c)
if n[1].kind == nkClosure: result = n[1] 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: else:
if owner.isIterator: if owner.isIterator:
if nfLL in n.flags: if nfLL in n.flags:
@ -757,6 +768,7 @@ proc liftLambdas*(g: ModuleGraph; fn: PSym, body: PNode; tooEarly: var bool): PN
if d.somethingToDo: if d.somethingToDo:
var c = initLiftingPass(fn) var c = initLiftingPass(fn)
result = liftCapturedVars(body, fn, d, c) result = liftCapturedVars(body, fn, d, c)
# echo renderTree(result, {renderIds})
if c.envvars.getOrDefault(fn.id) != nil: if c.envvars.getOrDefault(fn.id) != nil:
result = newTree(nkStmtList, rawClosureCreation(fn, d, c), result) result = newTree(nkStmtList, rawClosureCreation(fn, d, c), result)
else: else:

View file

@ -37,7 +37,7 @@ type
warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2, warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2,
warnUninit, warnGcMem, warnDestructor, warnLockLevel, warnResultShadowed, warnUninit, warnGcMem, warnDestructor, warnLockLevel, warnResultShadowed,
warnInconsistentSpacing, warnUser, warnInconsistentSpacing, warnUser,
hintSuccess, hintSuccessX, hintSuccess, hintSuccessX, hintCC,
hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded, hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded,
hintConvFromXtoItselfNotNeeded, hintExprAlwaysX, hintQuitCalled, hintConvFromXtoItselfNotNeeded, hintExprAlwaysX, hintQuitCalled,
hintProcessing, hintCodeBegin, hintCodeEnd, hintConf, hintPath, hintProcessing, hintCodeBegin, hintCodeEnd, hintConf, hintPath,
@ -94,6 +94,7 @@ const
warnUser: "$1", warnUser: "$1",
hintSuccess: "operation successful", hintSuccess: "operation successful",
hintSuccessX: "operation successful ($# lines compiled; $# sec total; $#; $#)", hintSuccessX: "operation successful ($# lines compiled; $# sec total; $#; $#)",
hintCC: "CC: \'$1\'", # unused
hintLineTooLong: "line too long", hintLineTooLong: "line too long",
hintXDeclaredButNotUsed: "'$1' is declared but not used", hintXDeclaredButNotUsed: "'$1' is declared but not used",
hintConvToBaseNotNeeded: "conversion to base object is not needed", hintConvToBaseNotNeeded: "conversion to base object is not needed",
@ -136,7 +137,7 @@ const
"Spacing", "User"] "Spacing", "User"]
HintsToStr* = [ HintsToStr* = [
"Success", "SuccessX", "LineTooLong", "Success", "SuccessX", "CC", "LineTooLong",
"XDeclaredButNotUsed", "ConvToBaseNotNeeded", "ConvFromXtoItselfNotNeeded", "XDeclaredButNotUsed", "ConvToBaseNotNeeded", "ConvFromXtoItselfNotNeeded",
"ExprAlwaysX", "QuitCalled", "Processing", "CodeBegin", "CodeEnd", "Conf", "ExprAlwaysX", "QuitCalled", "Processing", "CodeBegin", "CodeEnd", "Conf",
"Path", "CondTrue", "Name", "Pattern", "Exec", "Link", "Dependency", "Path", "CondTrue", "Name", "Pattern", "Exec", "Link", "Dependency",

View file

@ -45,13 +45,6 @@ when false:
if best.len > 0 and fileExists(res): if best.len > 0 and fileExists(res):
result = res result = res
const stdlibDirs = [
"pure", "core", "arch",
"pure/collections",
"pure/concurrency", "impure",
"wrappers", "wrappers/linenoise",
"windows", "posix", "js"]
when false: when false:
proc resolveDollar(project, source, pkg, subdir: string; info: TLineInfo): string = proc resolveDollar(project, source, pkg, subdir: string; info: TLineInfo): string =
template attempt(a) = template attempt(a) =
@ -120,7 +113,9 @@ proc getModuleName*(conf: ConfigRef; n: PNode): string =
case n.kind case n.kind
of nkStrLit, nkRStrLit, nkTripleStrLit: of nkStrLit, nkRStrLit, nkTripleStrLit:
try: 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: except ValueError:
localError(conf, n.info, "invalid path: " & n.strVal) localError(conf, n.info, "invalid path: " & n.strVal)
result = n.strVal result = n.strVal
@ -147,16 +142,9 @@ proc getModuleName*(conf: ConfigRef; n: PNode): string =
result = "" result = ""
else: else:
let modname = getModuleName(conf, n[2]) 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': # hacky way to implement 'x / y /../ z':
result = getModuleName(conf, n1) result = getModuleName(conf, n1)
result.add renderTree(n0, {renderNoComments}) result.add renderTree(n0, {renderNoComments}).replace(" ")
result.add modname result.add modname
of nkPrefix: of nkPrefix:
when false: when false:
@ -167,6 +155,7 @@ proc getModuleName*(conf: ConfigRef; n: PNode): string =
# hacky way to implement 'x / y /../ z': # hacky way to implement 'x / y /../ z':
result = renderTree(n, {renderNoComments}).replace(" ") result = renderTree(n, {renderNoComments}).replace(" ")
of nkDotExpr: of nkDotExpr:
localError(conf, n.info, warnDeprecated, "using '.' instead of '/' in import paths")
result = renderTree(n, {renderNoComments}).replace(".", "/") result = renderTree(n, {renderNoComments}).replace(".", "/")
of nkImportAs: of nkImportAs:
result = getModuleName(conf, n.sons[0]) result = getModuleName(conf, n.sons[0])

View file

@ -116,7 +116,8 @@ type
callOperator, callOperator,
parallel, parallel,
destructor, destructor,
notnil notnil,
dynamicBindSym
SymbolFilesOption* = enum SymbolFilesOption* = enum
disabledSf, writeOnlySf, readOnlySf, v2Sf disabledSf, writeOnlySf, readOnlySf, v2Sf
@ -406,8 +407,8 @@ proc mainCommandArg*(conf: ConfigRef): string =
proc existsConfigVar*(conf: ConfigRef; key: string): bool = proc existsConfigVar*(conf: ConfigRef; key: string): bool =
result = hasKey(conf.configVars, key) result = hasKey(conf.configVars, key)
proc getConfigVar*(conf: ConfigRef; key: string): string = proc getConfigVar*(conf: ConfigRef; key: string, default = ""): string =
result = conf.configVars.getOrDefault key result = conf.configVars.getOrDefault(key, default)
proc setConfigVar*(conf: ConfigRef; key, val: string) = proc setConfigVar*(conf: ConfigRef; key, val: string) =
conf.configVars[key] = val conf.configVars[key] = val
@ -556,13 +557,28 @@ proc findFile*(conf: ConfigRef; f: string; suppressStdlib = false): string {.pro
result = rawFindFile2(conf, f.toLowerAscii) result = rawFindFile2(conf, f.toLowerAscii)
patchModule(conf) 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 = proc findModule*(conf: ConfigRef; modulename, currentModule: string): string =
# returns path to module # returns path to module
const pkgPrefix = "pkg/" const pkgPrefix = "pkg/"
let m = addFileExt(modulename, NimExt) const stdPrefix = "std/"
var m = addFileExt(modulename, NimExt)
if m.startsWith(pkgPrefix): if m.startsWith(pkgPrefix):
result = findFile(conf, m.substr(pkgPrefix.len), suppressStdlib = true) result = findFile(conf, m.substr(pkgPrefix.len), suppressStdlib = true)
else: 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 let currentPath = currentModule.splitFile.dir
result = currentPath / m result = currentPath / m
if not existsFile(result): if not existsFile(result):

View file

@ -568,6 +568,7 @@ proc parsePar(p: var TParser): PNode =
result = newNodeP(nkPar, p) result = newNodeP(nkPar, p)
getTok(p) getTok(p)
optInd(p, result) optInd(p, result)
flexComment(p, result)
if p.tok.tokType in {tkDiscard, tkInclude, tkIf, tkWhile, tkCase, if p.tok.tokType in {tkDiscard, tkInclude, tkIf, tkWhile, tkCase,
tkTry, tkDefer, tkFinally, tkExcept, tkFor, tkBlock, tkTry, tkDefer, tkFinally, tkExcept, tkFor, tkBlock,
tkConst, tkLet, tkWhen, tkVar, tkConst, tkLet, tkWhen, tkVar,

View file

@ -627,15 +627,23 @@ proc gstmts(g: var TSrcGen, n: PNode, c: TContext, doIndent=true) =
gcoms(g) gcoms(g)
if doIndent: dedent(g) if doIndent: dedent(g)
else: else:
if rfLongMode in c.flags: indentNL(g) indentNL(g)
gsub(g, n) gsub(g, n)
gcoms(g) gcoms(g)
dedent(g)
optNL(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) = proc gif(g: var TSrcGen, n: PNode) =
var c: TContext var c: TContext
gsub(g, n.sons[0].sons[0]) gcond(g, n.sons[0].sons[0])
initContext(c) initContext(c)
putWithSpace(g, tkColon, ":") putWithSpace(g, tkColon, ":")
if longMode(g, n) or (lsub(g, n.sons[0].sons[1]) + g.lineLen > MaxLineLen): 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) = proc gwhile(g: var TSrcGen, n: PNode) =
var c: TContext var c: TContext
putWithSpace(g, tkWhile, "while") putWithSpace(g, tkWhile, "while")
gsub(g, n.sons[0]) gcond(g, n.sons[0])
putWithSpace(g, tkColon, ":") putWithSpace(g, tkColon, ":")
initContext(c) initContext(c)
if longMode(g, n) or (lsub(g, n.sons[1]) + g.lineLen > MaxLineLen): 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 var last = if n.sons[length-1].kind == nkElse: -2 else: -1
if longMode(g, n, 0, last): incl(c.flags, rfLongMode) if longMode(g, n, 0, last): incl(c.flags, rfLongMode)
putWithSpace(g, tkCase, "case") putWithSpace(g, tkCase, "case")
gsub(g, n.sons[0]) gcond(g, n.sons[0])
gcoms(g) gcoms(g)
optNL(g) optNL(g)
gsons(g, n, c, 1, last) 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): if longMode(g, n) or (lsub(g, n.sons[1]) + g.lineLen > MaxLineLen):
incl(c.flags, rfLongMode) incl(c.flags, rfLongMode)
gcoms(g) 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) gstmts(g, n.sons[1], c)
dedent(g)
proc gstaticStmt(g: var TSrcGen, n: PNode) = proc gstaticStmt(g: var TSrcGen, n: PNode) =
var c: TContext var c: TContext
@ -1104,13 +1109,13 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
put(g, tkAccent, "`") put(g, tkAccent, "`")
of nkIfExpr: of nkIfExpr:
putWithSpace(g, tkIf, "if") 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, ":") putWithSpace(g, tkColon, ":")
if n.len > 0: gsub(g, n.sons[0], 1) if n.len > 0: gsub(g, n.sons[0], 1)
gsons(g, n, emptyContext, 1) gsons(g, n, emptyContext, 1)
of nkElifExpr: of nkElifExpr:
putWithSpace(g, tkElif, " elif") putWithSpace(g, tkElif, " elif")
gsub(g, n, 0) gcond(g, n[0])
putWithSpace(g, tkColon, ":") putWithSpace(g, tkColon, ":")
gsub(g, n, 1) gsub(g, n, 1)
of nkElseExpr: of nkElseExpr:

View file

@ -608,16 +608,18 @@ proc myProcess(context: PPassContext, n: PNode): PNode =
rod.storeNode(c.graph, c.module, result) rod.storeNode(c.graph, c.module, result)
proc testExamples(c: PContext) = proc testExamples(c: PContext) =
let outputDir = c.config.getNimcacheDir / "runnableExamples"
createDir(outputDir)
let inp = toFullPath(c.config, c.module.info) 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" 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" let backend = if isDefined(c.config, "js"): "js"
elif isDefined(c.config, "cpp"): "cpp" elif isDefined(c.config, "cpp"): "cpp"
elif isDefined(c.config, "objc"): "objc" elif isDefined(c.config, "objc"): "objc"
else: "c" else: "c"
if os.execShellCmd(os.getAppFilename() & " " & backend & " --nimcache:" & nimcache & " -r " & outp) != 0: if os.execShellCmd(os.getAppFilename() & " " & backend & " --nimcache:" & nimcache & " -r " & outp) != 0:
quit "[Examples] failed" quit "[Examples] failed: see " & outp
else: else:
removeFile(outp) removeFile(outp)
removeFile(outp.changeFileExt(ExeExt)) removeFile(outp.changeFileExt(ExeExt))

View file

@ -505,7 +505,15 @@ proc explicitGenericSym(c: PContext, n: PNode, s: PSym): PNode =
for i in 1..sonsLen(n)-1: for i in 1..sonsLen(n)-1:
let formal = s.ast.sons[genericParamsPos].sons[i-1].typ 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) let tm = typeRel(m, formal, arg)
if tm in {isNone, isConvertible}: return nil if tm in {isNone, isConvertible}: return nil
var newInst = generateInstance(c, s, m.bindings, n.info) var newInst = generateInstance(c, s, m.bindings, n.info)

View file

@ -2031,9 +2031,10 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
c.runnableExamples = newTree(nkStmtList, c.runnableExamples = newTree(nkStmtList,
newTree(nkImportStmt, newStrNode(nkStrLit, expandFilename(inp)))) newTree(nkImportStmt, newStrNode(nkStrLit, expandFilename(inp))))
let imports = newTree(nkStmtList) 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 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) result = setMs(n, s)
else: else:
result = c.graph.emptyNode result = c.graph.emptyNode
@ -2126,7 +2127,7 @@ proc semSetConstr(c: PContext, n: PNode): PNode =
n.sons[i] = semExprWithType(c, n.sons[i]) n.sons[i] = semExprWithType(c, n.sons[i])
if typ == nil: if typ == nil:
typ = skipTypes(n.sons[i].typ, {tyGenericInst, tyVar, tyLent, tyOrdinal, tyAlias, tySink}) 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) localError(c.config, n.info, errOrdinalTypeExpected)
typ = makeRangeType(c, 0, MaxSetElements-1, n.info) typ = makeRangeType(c, 0, MaxSetElements-1, n.info)
elif lengthOrd(c.config, typ) > MaxSetElements: elif lengthOrd(c.config, typ) > MaxSetElements:

View file

@ -609,7 +609,7 @@ proc getConstExpr(m: PSym, n: PNode; g: ModuleGraph): PNode =
if computeSize(g.config, a.typ) < 0: if computeSize(g.config, a.typ) < 0:
localError(g.config, a.info, "cannot evaluate 'sizeof' because its type is not defined completely") localError(g.config, a.info, "cannot evaluate 'sizeof' because its type is not defined completely")
result = nil result = nil
elif skipTypes(a.typ, typedescInst+{tyRange}).kind in elif skipTypes(a.typ, typedescInst+{tyRange, tyArray}).kind in
IntegralTypes+NilableTypes+{tySet}: IntegralTypes+NilableTypes+{tySet}:
#{tyArray,tyObject,tyTuple}: #{tyArray,tyObject,tyTuple}:
result = newIntNodeT(getSize(g.config, a.typ), n, g) result = newIntNodeT(getSize(g.config, a.typ), n, g)

View file

@ -207,6 +207,67 @@ proc semBindSym(c: PContext, n: PNode): PNode =
else: else:
errorUndeclaredIdentifier(c, n.sons[1].info, sl.strVal) 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 semShallowCopy(c: PContext, n: PNode, flags: TExprFlags): PNode
proc semOf(c: PContext, n: PNode): 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 mOf: result = semOf(c, n)
of mHigh, mLow: result = semLowHigh(c, n, n[0].sym.magic) of mHigh, mLow: result = semLowHigh(c, n, n[0].sym.magic)
of mShallowCopy: result = semShallowCopy(c, n, flags) 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: of mProcCall:
result = n result = n
result.typ = n[1].typ result.typ = n[1].typ

View file

@ -11,6 +11,7 @@
# included from sem.nim # included from sem.nim
const const
errStringOrIdentNodeExpected = "string or ident node expected"
errStringLiteralExpected = "string literal expected" errStringLiteralExpected = "string literal expected"
errIntLiteralExpected = "integer literal expected" errIntLiteralExpected = "integer literal expected"
errWrongNumberOfVariables = "wrong number of variables" errWrongNumberOfVariables = "wrong number of variables"
@ -136,7 +137,7 @@ proc semSet(c: PContext, n: PNode, prev: PType): PType =
addSonSkipIntLit(result, base) addSonSkipIntLit(result, base)
if base.kind in {tyGenericInst, tyAlias, tySink}: base = lastSon(base) if base.kind in {tyGenericInst, tyAlias, tySink}: base = lastSon(base)
if base.kind != tyGenericParam: if base.kind != tyGenericParam:
if not isOrdinalType(base): if not isOrdinalType(base, allowEnumWithHoles = true):
localError(c.config, n.info, errOrdinalTypeExpected) localError(c.config, n.info, errOrdinalTypeExpected)
elif lengthOrd(c.config, base) > MaxSetElements: elif lengthOrd(c.config, base) > MaxSetElements:
localError(c.config, n.info, errSetTooBig) 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 mSet: result = semSet(c, n, prev)
of mOrdinal: result = semOrdinal(c, n, prev) of mOrdinal: result = semOrdinal(c, n, prev)
of mSeq: 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: 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: 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) result = semContainer(c, n, tySequence, "seq", prev)
if c.config.selectedGc == gcDestructors: if c.config.selectedGc == gcDestructors:
incl result.flags, tfHasAsgn incl result.flags, tfHasAsgn
@ -1603,8 +1598,14 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
result = prev result = prev
of nkSym: of nkSym:
let s = getGenSym(c, n.sym) let s = getGenSym(c, n.sym)
if s.kind == skType and s.typ != nil: if s.kind == skType and s.typ != nil or
var t = s.typ 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) let alias = maybeAliasType(c, t, prev)
if alias != nil: if alias != nil:
result = alias result = alias

View file

@ -498,9 +498,15 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
add(result, typeToString(t.sons[i])) add(result, typeToString(t.sons[i]))
result.add "]" result.add "]"
of tyAnd: 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: 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: of tyNot:
result = "not " & typeToString(t.sons[0]) result = "not " & typeToString(t.sons[0])
of tyExpr: of tyExpr:

View file

@ -616,19 +616,12 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
let rc = instr.regC let rc = instr.regC
case regs[ra].kind case regs[ra].kind
of rkNodeAddr: of rkNodeAddr:
# XXX: Workaround for vmgen bug:
let n = regs[rc].regToNode 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 # `var object` parameters are sent as rkNodeAddr. When they are mutated
# vmgen generates opcWrDeref, which means that we must dereference # vmgen generates opcWrDeref, which means that we must dereference
# twice. # twice.
# TODO: This should likely be handled differently in vmgen. # 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): nfIsRef notin n.flags):
regs[ra].nodeAddr[][] = n[] regs[ra].nodeAddr[][] = n[]
else: 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.typ.callConv == ccClosure and node.sons[0].kind == nkNilLit and
node.sons[1].kind == nkNilLit)) node.sons[1].kind == nkNilLit))
of opcNBindSym: of opcNBindSym:
# cannot use this simple check
# if dynamicBindSym notin c.config.features:
# bindSym with static input
decodeBx(rkNode) decodeBx(rkNode)
regs[ra].node = copyTree(c.constants.sons[rbx]) regs[ra].node = copyTree(c.constants.sons[rbx])
regs[ra].node.flags.incl nfIsRef 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: of opcNChild:
decodeBC(rkNode) decodeBC(rkNode)
let idx = regs[rc].intVal.int let idx = regs[rc].intVal.int
@ -1416,7 +1425,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
of opcNGetFile: of opcNGetFile:
decodeB(rkNode) decodeB(rkNode)
let n = regs[rb].node 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.info = n.info
regs[ra].node.typ = n.typ regs[ra].node.typ = n.typ
of opcNGetLine: of opcNGetLine:
@ -1780,7 +1789,7 @@ proc getGlobalValue*(c: PCtx; s: PSym): PNode =
include vmops include vmops
proc setupGlobalCtx(module: PSym; graph: ModuleGraph) = proc setupGlobalCtx*(module: PSym; graph: ModuleGraph) =
if graph.vm.isNil: if graph.vm.isNil:
graph.vm = newCtx(module, graph.cache, graph) graph.vm = newCtx(module, graph.cache, graph)
registerAdditionalOps(PCtx graph.vm) registerAdditionalOps(PCtx graph.vm)

View file

@ -136,7 +136,7 @@ type
opcLdGlobalAddr, # dest = addr(globals[Bx]) opcLdGlobalAddr, # dest = addr(globals[Bx])
opcLdImmInt, # dest = immediate value opcLdImmInt, # dest = immediate value
opcNBindSym, opcNBindSym, opcNDynBindSym,
opcSetType, # dest.typ = types[Bx] opcSetType, # dest.typ = types[Bx]
opcTypeTrait, opcTypeTrait,
opcMarshalLoad, opcMarshalStore, opcMarshalLoad, opcMarshalStore,
@ -229,7 +229,8 @@ proc refresh*(c: PCtx, module: PSym) =
c.prc = PProc(blocks: @[]) c.prc = PProc(blocks: @[])
c.loopIterations = MaxLoopIterations 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)) c.callbacks.add((name, callback))
const const

View file

@ -37,7 +37,9 @@ when hasFFI:
import evalffi import evalffi
type 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] TGenFlags = set[TGenFlag]
proc debugInfo(c: PCtx; info: TLineInfo): string = 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) = proc genAsgnPatch(c: PCtx; le: PNode, value: TRegister) =
case le.kind case le.kind
of nkBracketExpr: 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 idx = c.genIndex(le.sons[1], le.sons[0].typ)
c.gABC(le, opcWrArr, dest, idx, value) c.gABC(le, opcWrArr, dest, idx, value)
c.freeTemp(dest) c.freeTemp(dest)
@ -571,17 +573,17 @@ proc genAsgnPatch(c: PCtx; le: PNode, value: TRegister) =
of nkDotExpr, nkCheckedFieldExpr: of nkDotExpr, nkCheckedFieldExpr:
# XXX field checks here # XXX field checks here
let left = if le.kind == nkDotExpr: le else: le.sons[0] 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 idx = genField(c, left.sons[1])
c.gABC(left, opcWrObj, dest, idx, value) c.gABC(left, opcWrObj, dest, idx, value)
c.freeTemp(dest) c.freeTemp(dest)
of nkDerefExpr, nkHiddenDeref: 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.gABC(le, opcWrDeref, dest, 0, value)
c.freeTemp(dest) c.freeTemp(dest)
of nkSym: of nkSym:
if le.sym.isGlobal: if le.sym.isGlobal:
let dest = c.genx(le, {gfAddrOf}) let dest = c.genx(le, {gfNodeAddr})
c.gABC(le, opcWrDeref, dest, 0, value) c.gABC(le, opcWrDeref, dest, 0, value)
c.freeTemp(dest) c.freeTemp(dest)
else: else:
@ -815,6 +817,43 @@ proc genVoidABC(c: PCtx, n: PNode, dest: TDest, opcode: TOpcode) =
c.freeTemp(tmp2) c.freeTemp(tmp2)
c.freeTemp(tmp3) 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) = proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
case m case m
of mAnd: c.genAndOr(n, opcFJmp, dest) 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 mNNewNimNode: genBinaryABC(c, n, dest, opcNNewNimNode)
of mNCopyNimNode: genUnaryABC(c, n, dest, opcNCopyNimNode) of mNCopyNimNode: genUnaryABC(c, n, dest, opcNCopyNimNode)
of mNCopyNimTree: genUnaryABC(c, n, dest, opcNCopyNimTree) of mNCopyNimTree: genUnaryABC(c, n, dest, opcNCopyNimTree)
of mNBindSym: of mNBindSym: genBindSym(c, n, dest)
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 mStrToIdent: genUnaryABC(c, n, dest, opcStrToIdent) of mStrToIdent: genUnaryABC(c, n, dest, opcStrToIdent)
of mEqIdent: genBinaryABC(c, n, dest, opcEqIdent) of mEqIdent: genBinaryABC(c, n, dest, opcEqIdent)
of mEqNimrodNode: genBinaryABC(c, n, dest, opcEqNimrodNode) of mEqNimrodNode: genBinaryABC(c, n, dest, opcEqNimrodNode)
@ -1252,41 +1285,21 @@ proc canElimAddr(n: PNode): PNode =
# addr ( deref ( x )) --> x # addr ( deref ( x )) --> x
result = n.sons[0].sons[0] result = n.sons[0].sons[0]
proc genAddrDeref(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode; proc genAddr(c: PCtx, n: PNode, dest: var TDest, flags: TGenFlags) =
flags: TGenFlags) = if (let m = canElimAddr(n); m != nil):
# a nop for certain types
let isAddr = opc in {opcAddrNode, opcAddrReg}
if isAddr and (let m = canElimAddr(n); m != nil):
gen(c, m, dest, flags) gen(c, m, dest, flags)
return return
let af = if n[0].kind in {nkBracketExpr, nkDotExpr, nkCheckedFieldExpr}: {gfAddrOf, gfFieldAccess} let af = if n[0].kind in {nkBracketExpr, nkDotExpr, nkCheckedFieldExpr}: {gfNode}
else: {gfAddrOf} else: {gfNodeAddr}
let newflags = if isAddr: flags+af else: flags let newflags = flags-{gfNode, gfNodeAddr}+af
# consider:
# proc foo(f: var ref int) = if isGlobal(n.sons[0]):
# 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]):
gen(c, n.sons[0], dest, flags+af) gen(c, n.sons[0], dest, flags+af)
else: else:
let tmp = c.genx(n.sons[0], newflags) let tmp = c.genx(n.sons[0], newflags)
if dest < 0: dest = c.getTemp(n.typ) if dest < 0: dest = c.getTemp(n.typ)
if not isAddr: if c.prc.slots[tmp].kind >= slotTempUnknown:
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:
gABC(c, n, opcAddrNode, dest, tmp) gABC(c, n, opcAddrNode, dest, tmp)
# hack ahead; in order to fix bug #1781 we mark the temporary as # hack ahead; in order to fix bug #1781 we mark the temporary as
# permanent, so that it's not used for anything else: # 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) gABC(c, n, opcAddrReg, dest, tmp)
c.freeTemp(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 = proc whichAsgnOpc(n: PNode): TOpcode =
case n.typ.skipTypes(abstractRange-{tyTypeDesc}).kind case n.typ.skipTypes(abstractRange-{tyTypeDesc}).kind
of tyBool, tyChar, tyEnum, tyOrdinal, tyInt..tyInt64, tyUInt..tyUInt64: 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) = proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) =
case le.kind case le.kind
of nkBracketExpr: 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 idx = c.genIndex(le.sons[1], le.sons[0].typ)
let tmp = c.genx(ri) let tmp = c.genx(ri)
if le.sons[0].typ.skipTypes(abstractVarRange-{tyTypeDesc}).kind in { 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: of nkDotExpr, nkCheckedFieldExpr:
# XXX field checks here # XXX field checks here
let left = if le.kind == nkDotExpr: le else: le.sons[0] 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 idx = genField(c, left.sons[1])
let tmp = c.genx(ri) let tmp = c.genx(ri)
c.preventFalseAlias(left, opcWrObj, dest, idx, tmp) c.preventFalseAlias(left, opcWrObj, dest, idx, tmp)
c.freeTemp(tmp) c.freeTemp(tmp)
of nkDerefExpr, nkHiddenDeref: of nkDerefExpr, nkHiddenDeref:
let dest = c.genx(le.sons[0], {gfAddrOf}) let dest = c.genx(le.sons[0], {gfNode})
let tmp = c.genx(ri) let tmp = c.genx(ri)
c.preventFalseAlias(le, opcWrDeref, dest, 0, tmp) c.preventFalseAlias(le, opcWrDeref, dest, 0, tmp)
c.freeTemp(tmp) c.freeTemp(tmp)
@ -1409,7 +1435,7 @@ proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) =
checkCanEval(c, le) checkCanEval(c, le)
if s.isGlobal: if s.isGlobal:
withTemp(tmp, le.typ): withTemp(tmp, le.typ):
c.gen(le, tmp, {gfAddrOf}) c.gen(le, tmp, {gfNodeAddr})
let val = c.genx(ri) let val = c.genx(ri)
c.preventFalseAlias(le, opcWrDeref, tmp, 0, val) c.preventFalseAlias(le, opcWrDeref, tmp, 0, val)
c.freeTemp(val) c.freeTemp(val)
@ -1427,7 +1453,7 @@ proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) =
else: else:
gen(c, ri, dest) gen(c, ri, dest)
else: else:
let dest = c.genx(le, {gfAddrOf}) let dest = c.genx(le, {gfNodeAddr})
genAsgn(c, dest, ri, requiresCopy) genAsgn(c, dest, ri, requiresCopy)
proc genTypeLit(c: PCtx; t: PType; dest: var TDest) = proc genTypeLit(c: PCtx; t: PType; dest: var TDest) =
@ -1463,6 +1489,8 @@ proc genGlobalInit(c: PCtx; n: PNode; s: PSym) =
c.freeTemp(tmp) c.freeTemp(tmp)
proc genRdVar(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) = 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 let s = n.sym
if s.isGlobal: if s.isGlobal:
if sfCompileTime in s.flags or c.mode == emRepl: 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) else: genGlobalInit(c, n, s)
if dest < 0: dest = c.getTemp(n.typ) if dest < 0: dest = c.getTemp(n.typ)
assert s.typ != nil 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) var cc = c.getTemp(n.typ)
c.gABx(n, opcLdGlobal, cc, s.position) c.gABx(n, opcLdGlobal, cc, s.position)
c.gABC(n, opcNodeToReg, dest, cc) c.gABC(n, opcNodeToReg, dest, cc)
c.freeTemp(cc) c.freeTemp(cc)
elif {gfAddrOf, gfFieldAccess} * flags == {gfAddrOf}:
c.gABx(n, opcLdGlobalAddr, dest, s.position)
else: else:
c.gABx(n, opcLdGlobal, dest, s.position) c.gABx(n, opcLdGlobal, dest, s.position)
else: else:
@ -1498,7 +1526,8 @@ proc genRdVar(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
cannotEval(c, n) cannotEval(c, n)
template needsRegLoad(): untyped = 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; proc genArrAccess2(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode;
flags: TGenFlags) = flags: TGenFlags) =
@ -1634,7 +1663,7 @@ proc genVarSection(c: PCtx; n: PNode) =
c.globals.add(sa) c.globals.add(sa)
s.position = c.globals.len s.position = c.globals.len
if a.sons[2].kind != nkEmpty: 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]) let val = c.genx(a.sons[2])
c.genAdditionalCopy(a.sons[2], opcWrDeref, tmp, 0, val) c.genAdditionalCopy(a.sons[2], opcWrDeref, tmp, 0, val)
c.freeTemp(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 nkDotExpr: genObjAccess(c, n, dest, flags)
of nkCheckedFieldExpr: genCheckedObjAccess(c, n, dest, flags) of nkCheckedFieldExpr: genCheckedObjAccess(c, n, dest, flags)
of nkBracketExpr: genArrAccess(c, n, dest, flags) of nkBracketExpr: genArrAccess(c, n, dest, flags)
of nkDerefExpr, nkHiddenDeref: genAddrDeref(c, n, dest, opcLdDeref, flags) of nkDerefExpr, nkHiddenDeref: genDeref(c, n, dest, flags)
of nkAddr, nkHiddenAddr: genAddrDeref(c, n, dest, opcAddrNode, flags) of nkAddr, nkHiddenAddr: genAddr(c, n, dest, flags)
of nkIfStmt, nkIfExpr: genIf(c, n, dest) of nkIfStmt, nkIfExpr: genIf(c, n, dest)
of nkWhenStmt: of nkWhenStmt:
# This is "when nimvm" node. Chose the first branch. # This is "when nimvm" node. Chose the first branch.

View file

@ -55,6 +55,8 @@ doc.item.toc = """
# HTML rendered for doc.item's seeSrc variable. Note that this will render to # 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 # the empty string if you don't pass anything through --docSeeSrcURL. Available
# substitutaion variables here are: # 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. # * $path: relative path to the file being processed.
# * $line: line of the item in the original source file. # * $line: line of the item in the original source file.
# * $url: whatever you did pass through the --docSeeSrcUrl switch (which also # * $url: whatever you did pass through the --docSeeSrcUrl switch (which also
@ -62,7 +64,7 @@ doc.item.toc = """
doc.item.seesrc = """&nbsp;&nbsp;<a doc.item.seesrc = """&nbsp;&nbsp;<a
href="${url}/tree/${commit}/${path}#L${line}" href="${url}/tree/${commit}/${path}#L${line}"
class="link-seesrc" target="_blank">Source</a> 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 = """ doc.toc = """

View file

@ -6229,10 +6229,10 @@ imported:
:test: "nim c $1" :test: "nim c $1"
:status: 1 :status: 1
import strutils except `%`, toUpper import strutils except `%`, toUpperAscii
# doesn't work then: # 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. 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") echo su.format("$1", "lalelu")
The original module name is then not accessible. The The original module name is then not accessible. The notations
notations ``path/to/module`` or ``path.to.module`` or ``"path/to/module"`` ``path/to/module`` or ``"path/to/module"`` can be used to refer to a module
can be used to refer to a module in subdirectories: in subdirectories:
.. code-block:: nim .. 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: and so one **cannot** do:
.. code-block:: nim .. code-block:: nim
import lib.pure.strutils import lib/pure/strutils
echo lib.pure.strutils echo lib/pure/strutils.toUpperAscii("abc")
Likewise the following does not make sense as the name is ``strutils`` already: Likewise the following does not make sense as the name is ``strutils`` already:
.. code-block:: nim .. code-block:: nim
import lib.pure.strutils as strutils import lib/pure/strutils as strutils
Collective imports from a directory 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 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: There are two pseudo directories:

View file

@ -3,6 +3,7 @@
[Common] [Common]
cc=gcc # '=' and ':' are the same cc=gcc # '=' and ':' are the same
--foo="bar" # '--cc' and 'cc' are the same, 'bar' and '"bar"' are the same
--verbose --verbose
[Windows] [Windows]

View file

@ -50,6 +50,7 @@ Boot options:
Commands for core developers: Commands for core developers:
web [options] generates the website and the full documentation web [options] generates the website and the full documentation
(see `nimweb.nim` for cmd line options)
website [options] generates only the website website [options] generates only the website
csource -d:release builds the C sources for installation csource -d:release builds the C sources for installation
pdf builds the PDF documentation 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" % exec("$# --var:version=$# --var:mingw=none --main:compiler/nim.nim zip compiler/installer.ini" %
["tools/niminst/niminst".exe, VersionAsString]) ["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) = proc xz(args: string) =
ensureCleanGit()
bundleNimbleSrc() bundleNimbleSrc()
bundleNimsuggest(false) bundleNimsuggest(false)
nimexec("cc -r $2 --var:version=$1 --var:mingw=none --main:compiler/nim.nim scripts compiler/installer.ini" % nimexec("cc -r $2 --var:version=$1 --var:mingw=none --main:compiler/nim.nim scripts compiler/installer.ini" %

View file

@ -382,16 +382,24 @@ type
{.deprecated: [TBindSymRule: BindSymRule].} {.deprecated: [TBindSymRule: BindSymRule].}
proc bindSym*(ident: static[string], rule: BindSymRule = brClosed): NimNode {. proc bindSym*(ident: string | NimNode, rule: BindSymRule = brClosed): NimNode {.
magic: "NBindSym", noSideEffect.} magic: "NBindSym", noSideEffect.}
## creates a node that binds `ident` to a symbol node. The bound symbol ## creates a node that binds `ident` to a symbol node. The bound symbol
## may be an overloaded symbol. ## may be an overloaded symbol.
## if `ident` is a NimNode, it must have nkIdent kind.
## If ``rule == brClosed`` either an ``nkClosedSymChoice`` tree is ## If ``rule == brClosed`` either an ``nkClosedSymChoice`` tree is
## returned or ``nkSym`` if the symbol is not ambiguous. ## returned or ``nkSym`` if the symbol is not ambiguous.
## If ``rule == brOpen`` either an ``nkOpenSymChoice`` tree is ## If ``rule == brOpen`` either an ``nkOpenSymChoice`` tree is
## returned or ``nkSym`` if the symbol is not ambiguous. ## returned or ``nkSym`` if the symbol is not ambiguous.
## If ``rule == brForceOpen`` always an ``nkOpenSymChoice`` tree is ## If ``rule == brForceOpen`` always an ``nkOpenSymChoice`` tree is
## returned even if the symbol is not ambiguous. ## 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 {. proc genSym*(kind: NimSymKind = nskLet; ident = ""): NimNode {.
magic: "NGenSym", noSideEffect.} magic: "NGenSym", noSideEffect.}
@ -425,6 +433,7 @@ proc getColumn(arg: NimNode): int {.magic: "NLineInfo", noSideEffect.}
proc getFile(arg: NimNode): string {.magic: "NLineInfo", noSideEffect.} proc getFile(arg: NimNode): string {.magic: "NLineInfo", noSideEffect.}
proc lineInfoObj*(n: NimNode): LineInfo {.compileTime.} = proc lineInfoObj*(n: NimNode): LineInfo {.compileTime.} =
## returns ``LineInfo`` of ``n``, using absolute path for ``filename``
result.filename = n.getFile result.filename = n.getFile
result.line = n.getLine result.line = n.getLine
result.column = n.getColumn result.column = n.getColumn
@ -1284,7 +1293,7 @@ proc customPragmaNode(n: NimNode): NimNode =
let let
typ = n.getTypeInst() 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] return typ[1][1]
elif typ.typeKind == ntyTypeDesc: elif typ.typeKind == ntyTypeDesc:
let impl = typ[1].getImpl() let impl = typ[1].getImpl()
@ -1319,6 +1328,8 @@ proc customPragmaNode(n: NimNode): NimNode =
if identDefs.kind == nnkRecCase: if identDefs.kind == nnkRecCase:
identDefsStack.add(identDefs[0]) identDefsStack.add(identDefs[0])
for i in 1..<identDefs.len: 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: if identDefs[i][1].kind == nnkIdentDefs:
identDefsStack.add(identDefs[i][1]) identDefsStack.add(identDefs[i][1])
else: # nnkRecList else: # nnkRecList

View file

@ -449,10 +449,11 @@ proc generateSymbolIndex(symbols: seq[IndexEntry]): string =
desc = if not symbols[j].linkDesc.isNil: symbols[j].linkDesc else: "" desc = if not symbols[j].linkDesc.isNil: symbols[j].linkDesc else: ""
if desc.len > 0: if desc.len > 0:
result.addf("""<li><a class="reference external" 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]) """, [url, text, desc])
else: 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]) """, [url, text])
inc j inc j
result.add("</ul></dd>\n") 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. # Build a list of levels and extracted titles to make processing easier.
var var
titleRef: string titleRef: string
titleTag: string
levels: seq[tuple[level: int, text: string]] levels: seq[tuple[level: int, text: string]]
L = 0 L = 0
level = 1 level = 1
@ -519,10 +521,12 @@ proc generateDocumentationTOC(entries: seq[IndexEntry]): string =
let link = entries[L].link let link = entries[L].link
if link.isDocumentationTitle: if link.isDocumentationTitle:
titleRef = link titleRef = link
titleTag = levels[L].text
else: else:
result.add(level.indentToLevel(levels[L].level)) result.add(level.indentToLevel(levels[L].level))
result.add("<li><a href=\"" & link & "\">" & result.addf("""<li><a class="reference" data-doc-search-tag="$1" href="$2">
levels[L].text & "</a></li>\n") $3</a></li>
""", [titleTag & " : " & levels[L].text, link, levels[L].text])
inc L inc L
result.add(level.indentToLevel(1) & "</ul>\n") result.add(level.indentToLevel(1) & "</ul>\n")
assert(not titleRef.isNil, assert(not titleRef.isNil,

View file

@ -854,20 +854,20 @@ when isMainModule:
doAssert numbers.distribute(6)[0] == @[1, 2] doAssert numbers.distribute(6)[0] == @[1, 2]
doAssert numbers.distribute(6)[5] == @[7] doAssert numbers.distribute(6)[5] == @[7]
let a = @[1, 2, 3, 4, 5, 6, 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, true) == @[@[1, 2, 3, 4, 5, 6, 7]]
doAssert a.distribute(1, false) == @[@[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, true) == @[@[1, 2, 3, 4], @[5, 6, 7]]
doAssert a.distribute(2, false) == @[@[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, true) == @[@[1, 2, 3], @[4, 5], @[6, 7]]
doAssert a.distribute(3, false) == @[@[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, true) == @[@[1, 2], @[3, 4], @[5, 6], @[7]]
doAssert a.distribute(4, false) == @[@[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, true) == @[@[1, 2], @[3, 4], @[5], @[6], @[7]]
doAssert a.distribute(5, false) == @[@[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, true) == @[@[1, 2], @[3], @[4], @[5], @[6], @[7]]
doAssert a.distribute(6, false) == @[ doAssert a.distribute(6, false) == @[
@[1, 2], @[3, 4], @[5, 6], @[7], @[], @[]] @[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) doAssert a.distribute(90, false) == a.distribute(90, true)
var b = @[0] var b = @[0]
for f in 1 .. 25: b.add(f) for f in 1 .. 25: b.add(f)

View file

@ -347,6 +347,18 @@ proc excl*[A](s: var HashSet[A], other: HashSet[A]) =
assert other.isValid, "The set `other` needs to be initialized." assert other.isValid, "The set `other` needs to be initialized."
for item in other: discard exclImpl(s, item) 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 = proc containsOrIncl*[A](s: var HashSet[A], key: A): bool =
## Includes `key` in the set `s` and tells if `key` was added to `s`. ## Includes `key` in the set `s` and tells if `key` was added to `s`.
## ##

View file

@ -332,7 +332,7 @@ when defined(windows):
if s.len == 0: return "" if s.len == 0: return ""
# educated guess of capacity: # educated guess of capacity:
var cap = s.len + s.len shr 2 var cap = s.len + s.len shr 2
result = newStringOfCap(cap*2) result = newString(cap*2)
# convert to utf-16 LE # convert to utf-16 LE
var m = multiByteToWideChar(codePage = c.src, dwFlags = 0'i32, var m = multiByteToWideChar(codePage = c.src, dwFlags = 0'i32,
lpMultiByteStr = cstring(s), lpMultiByteStr = cstring(s),
@ -347,7 +347,7 @@ when defined(windows):
lpWideCharStr = nil, lpWideCharStr = nil,
cchWideChar = cint(0)) cchWideChar = cint(0))
# and do the conversion properly: # and do the conversion properly:
result = newStringOfCap(cap*2) result = newString(cap*2)
m = multiByteToWideChar(codePage = c.src, dwFlags = 0'i32, m = multiByteToWideChar(codePage = c.src, dwFlags = 0'i32,
lpMultiByteStr = cstring(s), lpMultiByteStr = cstring(s),
cbMultiByte = cint(s.len), cbMultiByte = cint(s.len),
@ -364,7 +364,7 @@ when defined(windows):
if int(c.dest) == 1200: return if int(c.dest) == 1200: return
# otherwise the fun starts again: # otherwise the fun starts again:
cap = s.len + s.len shr 2 cap = s.len + s.len shr 2
var res = newStringOfCap(cap) var res = newString(cap)
m = wideCharToMultiByte( m = wideCharToMultiByte(
codePage = c.dest, codePage = c.dest,
dwFlags = 0'i32, dwFlags = 0'i32,
@ -382,7 +382,7 @@ when defined(windows):
lpMultiByteStr = nil, lpMultiByteStr = nil,
cbMultiByte = cint(0)) cbMultiByte = cint(0))
# and do the conversion properly: # and do the conversion properly:
res = newStringOfCap(cap) res = newString(cap)
m = wideCharToMultiByte( m = wideCharToMultiByte(
codePage = c.dest, codePage = c.dest,
dwFlags = 0'i32, dwFlags = 0'i32,

View file

@ -60,7 +60,7 @@ proc raiseOSError*(errorCode: OSErrorCode; additionalInfo = "") {.noinline.} =
if additionalInfo.len == 0: if additionalInfo.len == 0:
e.msg = osErrorMsg(errorCode) e.msg = osErrorMsg(errorCode)
else: else:
e.msg = osErrorMsg(errorCode) & "\nAdditional info: " & additionalInfo e.msg = osErrorMsg(errorCode) & "\nAdditional info: '" & additionalInfo & "'"
if e.msg == "": if e.msg == "":
e.msg = "unknown OS error" e.msg = "unknown OS error"
raise e raise e

View file

@ -92,6 +92,9 @@ proc newSelector*[T](): Selector[T] =
result.maxFD = maxFD result.maxFD = maxFD
result.fds = newSeq[SelectorKey[T]](maxFD) result.fds = newSeq[SelectorKey[T]](maxFD)
for i in 0 ..< maxFD:
result.fds[i].ident = InvalidIdent
proc close*[T](s: Selector[T]) = proc close*[T](s: Selector[T]) =
let res = posix.close(s.epollFD) let res = posix.close(s.epollFD)
when hasThreadSupport: when hasThreadSupport:
@ -100,12 +103,6 @@ proc close*[T](s: Selector[T]) =
if res != 0: if res != 0:
raiseIOSelectorsError(osLastError()) raiseIOSelectorsError(osLastError())
template clearKey[T](key: ptr SelectorKey[T]) =
var empty: T
key.ident = 0
key.events = {}
key.data = empty
proc newSelectEvent*(): SelectEvent = proc newSelectEvent*(): SelectEvent =
let fdci = eventfd(0, 0) let fdci = eventfd(0, 0)
if fdci == -1: if fdci == -1:
@ -135,7 +132,7 @@ proc registerHandle*[T](s: Selector[T], fd: int | SocketHandle,
events: set[Event], data: T) = events: set[Event], data: T) =
let fdi = int(fd) let fdi = int(fd)
s.checkFd(fdi) 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) s.setKey(fdi, events, 0, data)
if events != {}: if events != {}:
var epv = EpollEvent(events: EPOLLRDHUP) 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) let fdi = int(fd)
s.checkFd(fdi) s.checkFd(fdi)
var pkey = addr(s.fds[fdi]) var pkey = addr(s.fds[fdi])
doAssert(pkey.ident != 0, doAssert(pkey.ident != InvalidIdent,
"Descriptor $# is not registered in the selector!" % $fdi) "Descriptor $# is not registered in the selector!" % $fdi)
doAssert(pkey.events * maskEvents == {}) doAssert(pkey.events * maskEvents == {})
if pkey.events != events: if pkey.events != events:
@ -180,7 +177,7 @@ proc unregister*[T](s: Selector[T], fd: int|SocketHandle) =
let fdi = int(fd) let fdi = int(fd)
s.checkFd(fdi) s.checkFd(fdi)
var pkey = addr(s.fds[fdi]) var pkey = addr(s.fds[fdi])
doAssert(pkey.ident != 0, doAssert(pkey.ident != InvalidIdent,
"Descriptor $# is not registered in the selector!" % $fdi) "Descriptor $# is not registered in the selector!" % $fdi)
if pkey.events != {}: if pkey.events != {}:
when not defined(android): when not defined(android):
@ -243,7 +240,7 @@ proc unregister*[T](s: Selector[T], ev: SelectEvent) =
let fdi = int(ev.efd) let fdi = int(ev.efd)
s.checkFd(fdi) s.checkFd(fdi)
var pkey = addr(s.fds[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) doAssert(Event.User in pkey.events)
var epv = EpollEvent() var epv = EpollEvent()
if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fdi.cint, addr epv) != 0: 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) setNonBlocking(fdi.cint)
s.checkFd(fdi) s.checkFd(fdi)
doAssert(s.fds[fdi].ident == 0) doAssert(s.fds[fdi].ident == InvalidIdent)
var events = {Event.Timer} var events = {Event.Timer}
var epv = EpollEvent(events: EPOLLIN or EPOLLRDHUP) var epv = EpollEvent(events: EPOLLIN or EPOLLRDHUP)
@ -307,7 +304,7 @@ when not defined(android):
setNonBlocking(fdi.cint) setNonBlocking(fdi.cint)
s.checkFd(fdi) s.checkFd(fdi)
doAssert(s.fds[fdi].ident == 0) doAssert(s.fds[fdi].ident == InvalidIdent)
var epv = EpollEvent(events: EPOLLIN or EPOLLRDHUP) var epv = EpollEvent(events: EPOLLIN or EPOLLRDHUP)
epv.data.u64 = fdi.uint epv.data.u64 = fdi.uint
@ -334,7 +331,7 @@ when not defined(android):
setNonBlocking(fdi.cint) setNonBlocking(fdi.cint)
s.checkFd(fdi) s.checkFd(fdi)
doAssert(s.fds[fdi].ident == 0) doAssert(s.fds[fdi].ident == InvalidIdent)
var epv = EpollEvent(events: EPOLLIN or EPOLLRDHUP) var epv = EpollEvent(events: EPOLLIN or EPOLLRDHUP)
epv.data.u64 = fdi.uint epv.data.u64 = fdi.uint
@ -347,7 +344,7 @@ when not defined(android):
proc registerEvent*[T](s: Selector[T], ev: SelectEvent, data: T) = proc registerEvent*[T](s: Selector[T], ev: SelectEvent, data: T) =
let fdi = int(ev.efd) 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) s.setKey(fdi, {Event.User}, 0, data)
var epv = EpollEvent(events: EPOLLIN or EPOLLRDHUP) var epv = EpollEvent(events: EPOLLIN or EPOLLRDHUP)
epv.data.u64 = ev.efd.uint 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 fdi = int(resTable[i].data.u64)
let pevents = resTable[i].events let pevents = resTable[i].events
var pkey = addr(s.fds[fdi]) var pkey = addr(s.fds[fdi])
doAssert(pkey.ident != 0) doAssert(pkey.ident != InvalidIdent)
var rkey = ReadyKey(fd: fdi, events: {}) var rkey = ReadyKey(fd: fdi, events: {})
if (pevents and EPOLLERR) != 0 or (pevents and EPOLLHUP) != 0: if (pevents and EPOLLERR) != 0 or (pevents and EPOLLHUP) != 0:
@ -482,7 +479,7 @@ template isEmpty*[T](s: Selector[T]): bool =
(s.count == 0) (s.count == 0)
proc contains*[T](s: Selector[T], fd: SocketHandle|int): bool {.inline.} = 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 = proc getData*[T](s: Selector[T], fd: SocketHandle|int): var T =
let fdi = int(fd) let fdi = int(fd)

View file

@ -114,6 +114,9 @@ proc newSelector*[T](): Selector[T] =
result.fds = newSeq[SelectorKey[T]](maxFD) result.fds = newSeq[SelectorKey[T]](maxFD)
result.changes = newSeqOfCap[KEvent](MAX_KQUEUE_EVENTS) result.changes = newSeqOfCap[KEvent](MAX_KQUEUE_EVENTS)
for i in 0 ..< maxFD:
result.fds[i].ident = InvalidIdent
result.sock = usock result.sock = usock
result.kqFD = kqFD result.kqFD = kqFD
result.maxFD = maxFD.int result.maxFD = maxFD.int
@ -128,12 +131,6 @@ proc close*[T](s: Selector[T]) =
if res1 != 0 or res2 != 0: if res1 != 0 or res2 != 0:
raiseIOSelectorsError(osLastError()) raiseIOSelectorsError(osLastError())
template clearKey[T](key: ptr SelectorKey[T]) =
var empty: T
key.ident = 0
key.events = {}
key.data = empty
proc newSelectEvent*(): SelectEvent = proc newSelectEvent*(): SelectEvent =
var fds: array[2, cint] var fds: array[2, cint]
if posix.pipe(fds) != 0: if posix.pipe(fds) != 0:
@ -221,7 +218,7 @@ proc registerHandle*[T](s: Selector[T], fd: int | SocketHandle,
events: set[Event], data: T) = events: set[Event], data: T) =
let fdi = int(fd) let fdi = int(fd)
s.checkFd(fdi) s.checkFd(fdi)
doAssert(s.fds[fdi].ident == 0) doAssert(s.fds[fdi].ident == InvalidIdent)
s.setKey(fdi, events, 0, data) s.setKey(fdi, events, 0, data)
if events != {}: if events != {}:
@ -242,7 +239,7 @@ proc updateHandle*[T](s: Selector[T], fd: int | SocketHandle,
let fdi = int(fd) let fdi = int(fd)
s.checkFd(fdi) s.checkFd(fdi)
var pkey = addr(s.fds[fdi]) var pkey = addr(s.fds[fdi])
doAssert(pkey.ident != 0, doAssert(pkey.ident != InvalidIdent,
"Descriptor $# is not registered in the queue!" % $fdi) "Descriptor $# is not registered in the queue!" % $fdi)
doAssert(pkey.events * maskEvents == {}) doAssert(pkey.events * maskEvents == {})
@ -269,7 +266,7 @@ proc registerTimer*[T](s: Selector[T], timeout: int, oneshot: bool,
data: T): int {.discardable.} = data: T): int {.discardable.} =
let fdi = getUnique(s) let fdi = getUnique(s)
s.checkFd(fdi) 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 events = if oneshot: {Event.Timer, Event.Oneshot} else: {Event.Timer}
let flags: cushort = if oneshot: EV_ONESHOT or EV_ADD else: EV_ADD 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.} = data: T): int {.discardable.} =
let fdi = getUnique(s) let fdi = getUnique(s)
s.checkFd(fdi) s.checkFd(fdi)
doAssert(s.fds[fdi].ident == 0) doAssert(s.fds[fdi].ident == InvalidIdent)
s.setKey(fdi, {Event.Signal}, signal, data) s.setKey(fdi, {Event.Signal}, signal, data)
var nmask, omask: Sigset var nmask, omask: Sigset
@ -315,7 +312,7 @@ proc registerProcess*[T](s: Selector[T], pid: int,
data: T): int {.discardable.} = data: T): int {.discardable.} =
let fdi = getUnique(s) let fdi = getUnique(s)
s.checkFd(fdi) s.checkFd(fdi)
doAssert(s.fds[fdi].ident == 0) doAssert(s.fds[fdi].ident == InvalidIdent)
var kflags: cushort = EV_ONESHOT or EV_ADD var kflags: cushort = EV_ONESHOT or EV_ADD
setKey(s, fdi, {Event.Process, Event.Oneshot}, pid, data) 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) = proc registerEvent*[T](s: Selector[T], ev: SelectEvent, data: T) =
let fdi = ev.rfd.int 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) setKey(s, fdi, {Event.User}, 0, data)
modifyKQueue(s, fdi.uint, EVFILT_READ, EV_ADD, 0, 0, nil) 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) let fdi = int(fd)
s.checkFd(fdi) s.checkFd(fdi)
var pkey = addr(s.fds[fdi]) var pkey = addr(s.fds[fdi])
doAssert(pkey.ident != 0, doAssert(pkey.ident != InvalidIdent,
"Descriptor [" & $fdi & "] is not registered in the queue!") "Descriptor [" & $fdi & "] is not registered in the queue!")
if pkey.events != {}: if pkey.events != {}:
@ -434,7 +431,7 @@ proc unregister*[T](s: Selector[T], ev: SelectEvent) =
let fdi = int(ev.rfd) let fdi = int(ev.rfd)
s.checkFd(fdi) s.checkFd(fdi)
var pkey = addr(s.fds[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) doAssert(Event.User in pkey.events)
modifyKQueue(s, uint(fdi), EVFILT_READ, EV_DELETE, 0, 0, nil) modifyKQueue(s, uint(fdi), EVFILT_READ, EV_DELETE, 0, 0, nil)
when not declared(CACHE_EVENTS): when not declared(CACHE_EVENTS):
@ -593,7 +590,7 @@ template isEmpty*[T](s: Selector[T]): bool =
(s.count == 0) (s.count == 0)
proc contains*[T](s: Selector[T], fd: SocketHandle|int): bool {.inline.} = 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 = proc getData*[T](s: Selector[T], fd: SocketHandle|int): var T =
let fdi = int(fd) let fdi = int(fd)

View file

@ -70,6 +70,9 @@ proc newSelector*[T](): Selector[T] =
result.fds = newSeq[SelectorKey[T]](maxFD) result.fds = newSeq[SelectorKey[T]](maxFD)
result.pollfds = newSeq[TPollFd](maxFD) result.pollfds = newSeq[TPollFd](maxFD)
for i in 0 ..< maxFD:
result.fds[i].ident = InvalidIdent
proc close*[T](s: Selector[T]) = proc close*[T](s: Selector[T]) =
when hasThreadSupport: when hasThreadSupport:
deinitLock(s.lock) deinitLock(s.lock)
@ -77,12 +80,6 @@ proc close*[T](s: Selector[T]) =
deallocSharedArray(s.pollfds) deallocSharedArray(s.pollfds)
deallocShared(cast[pointer](s)) 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]) = template pollAdd[T](s: Selector[T], sock: cint, events: set[Event]) =
withPollLock(s): withPollLock(s):
var pollev: cshort = 0 var pollev: cshort = 0
@ -135,7 +132,7 @@ proc registerHandle*[T](s: Selector[T], fd: int | SocketHandle,
events: set[Event], data: T) = events: set[Event], data: T) =
var fdi = int(fd) var fdi = int(fd)
s.checkFd(fdi) s.checkFd(fdi)
doAssert(s.fds[fdi].ident == 0) doAssert(s.fds[fdi].ident == InvalidIdent)
setKey(s, fdi, events, 0, data) setKey(s, fdi, events, 0, data)
if events != {}: s.pollAdd(fdi.cint, events) if events != {}: s.pollAdd(fdi.cint, events)
@ -146,7 +143,7 @@ proc updateHandle*[T](s: Selector[T], fd: int | SocketHandle,
let fdi = int(fd) let fdi = int(fd)
s.checkFd(fdi) s.checkFd(fdi)
var pkey = addr(s.fds[fdi]) var pkey = addr(s.fds[fdi])
doAssert(pkey.ident != 0, doAssert(pkey.ident != InvalidIdent,
"Descriptor [" & $fdi & "] is not registered in the queue!") "Descriptor [" & $fdi & "] is not registered in the queue!")
doAssert(pkey.events * maskEvents == {}) 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) = proc registerEvent*[T](s: Selector[T], ev: SelectEvent, data: T) =
var fdi = int(ev.rfd) 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} var events = {Event.User}
setKey(s, fdi, events, 0, data) setKey(s, fdi, events, 0, data)
events.incl(Event.Read) events.incl(Event.Read)
@ -172,9 +169,9 @@ proc unregister*[T](s: Selector[T], fd: int|SocketHandle) =
let fdi = int(fd) let fdi = int(fd)
s.checkFd(fdi) s.checkFd(fdi)
var pkey = addr(s.fds[fdi]) var pkey = addr(s.fds[fdi])
doAssert(pkey.ident != 0, doAssert(pkey.ident != InvalidIdent,
"Descriptor [" & $fdi & "] is not registered in the queue!") "Descriptor [" & $fdi & "] is not registered in the queue!")
pkey.ident = 0 pkey.ident = InvalidIdent
pkey.events = {} pkey.events = {}
s.pollRemove(fdi.cint) s.pollRemove(fdi.cint)
@ -182,9 +179,9 @@ proc unregister*[T](s: Selector[T], ev: SelectEvent) =
let fdi = int(ev.rfd) let fdi = int(ev.rfd)
s.checkFd(fdi) s.checkFd(fdi)
var pkey = addr(s.fds[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) doAssert(Event.User in pkey.events)
pkey.ident = 0 pkey.ident = InvalidIdent
pkey.events = {} pkey.events = {}
s.pollRemove(fdi.cint) s.pollRemove(fdi.cint)
@ -270,7 +267,7 @@ template isEmpty*[T](s: Selector[T]): bool =
(s.count == 0) (s.count == 0)
proc contains*[T](s: Selector[T], fd: SocketHandle|int): bool {.inline.} = 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 = proc getData*[T](s: Selector[T], fd: SocketHandle|int): var T =
let fdi = int(fd) let fdi = int(fd)

View file

@ -99,6 +99,9 @@ proc newSelector*[T](): Selector[T] =
result = Selector[T]() result = Selector[T]()
result.fds = newSeq[SelectorKey[T]](FD_SETSIZE) 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.rSet)
IOFD_ZERO(addr result.wSet) IOFD_ZERO(addr result.wSet)
IOFD_ZERO(addr result.eSet) IOFD_ZERO(addr result.eSet)
@ -195,7 +198,7 @@ proc setSelectKey[T](s: Selector[T], fd: SocketHandle, events: set[Event],
var i = 0 var i = 0
let fdi = int(fd) let fdi = int(fd)
while i < FD_SETSIZE: while i < FD_SETSIZE:
if s.fds[i].ident == 0: if s.fds[i].ident == InvalidIdent:
var pkey = addr(s.fds[i]) var pkey = addr(s.fds[i])
pkey.ident = fdi pkey.ident = fdi
pkey.events = events pkey.events = events
@ -221,7 +224,7 @@ proc delKey[T](s: Selector[T], fd: SocketHandle) =
var i = 0 var i = 0
while i < FD_SETSIZE: while i < FD_SETSIZE:
if s.fds[i].ident == fd.int: if s.fds[i].ident == fd.int:
s.fds[i].ident = 0 s.fds[i].ident = InvalidIdent
s.fds[i].events = {} s.fds[i].events = {}
s.fds[i].data = empty s.fds[i].data = empty
break break
@ -335,7 +338,7 @@ proc selectInto*[T](s: Selector[T], timeout: int,
var k = 0 var k = 0
while (i < FD_SETSIZE) and (k < count): while (i < FD_SETSIZE) and (k < count):
if s.fds[i].ident != 0: if s.fds[i].ident != InvalidIdent:
var flag = false var flag = false
var pkey = addr(s.fds[i]) var pkey = addr(s.fds[i])
var rkey = ReadyKey(fd: int(pkey.ident), events: {}) var rkey = ReadyKey(fd: int(pkey.ident), events: {})

View file

@ -1004,6 +1004,13 @@ proc processElseBranch(recCaseNode, elseBranch, jsonNode, kindType,
exprColonExpr.add(ifStmt) exprColonExpr.add(ifStmt)
proc createConstructor(typeSym, jsonNode: NimNode): NimNode {.compileTime.} 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] = proc processObjField(field, jsonNode: NimNode): seq[NimNode] =
## Process a field from a ``RecList``. ## Process a field from a ``RecList``.
## ##
@ -1022,8 +1029,8 @@ proc processObjField(field, jsonNode: NimNode): seq[NimNode] =
# Add the field value. # Add the field value.
# -> jsonNode["`field`"] # -> jsonNode["`field`"]
let indexedJsonNode = createJsonIndexer(jsonNode, $field) let indexedJsonNode = createJsonIndexer(jsonNode, $field)
exprColonExpr.add(createConstructor(getTypeInst(field), indexedJsonNode)) let typeNode = detectDistinctType(field)
exprColonExpr.add(createConstructor(typeNode, indexedJsonNode))
of nnkRecCase: of nnkRecCase:
# A "case" field that introduces a variant. # A "case" field that introduces a variant.
let exprColonExpr = newNimNode(nnkExprColonExpr) let exprColonExpr = newNimNode(nnkExprColonExpr)
@ -1248,7 +1255,7 @@ proc createConstructor(typeSym, jsonNode: NimNode): NimNode =
let seqT = typeSym[1] let seqT = typeSym[1]
let forLoopI = genSym(nskForVar, "i") let forLoopI = genSym(nskForVar, "i")
let indexerNode = createJsonIndexer(jsonNode, forLoopI) let indexerNode = createJsonIndexer(jsonNode, forLoopI)
let constructorNode = createConstructor(seqT, indexerNode) let constructorNode = createConstructor(detectDistinctType(seqT), indexerNode)
# Create a statement expression containing a for loop. # Create a statement expression containing a for loop.
result = quote do: result = quote do:
@ -1284,7 +1291,10 @@ proc createConstructor(typeSym, jsonNode: NimNode): NimNode =
# Handle all other types. # Handle all other types.
let obj = getType(typeSym) 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`. # When `Sym "Foo"` turns out to be a `ref object`.
result = createConstructor(obj, jsonNode) result = createConstructor(obj, jsonNode)
else: else:
@ -1295,6 +1305,21 @@ proc createConstructor(typeSym, jsonNode: NimNode): NimNode =
# TODO: The fact that `jsonNode` here works to give a good line number # TODO: The fact that `jsonNode` here works to give a good line number
# is weird. Specifying typeSym should work but doesn't. # is weird. Specifying typeSym should work but doesn't.
error("Use a named tuple instead of: " & $toStrLit(typeSym), jsonNode) 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: else:
doAssert false, "Unable to create constructor for: " & $typeSym.kind 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.person.age == 21
## doAssert data.list == @[1, 2, 3, 4] ## doAssert data.list == @[1, 2, 3, 4]
let typeNode = getTypeInst(T) let typeNode = getTypeImpl(T)
expectKind(typeNode, nnkBracketExpr) expectKind(typeNode, nnkBracketExpr)
doAssert(($typeNode[0]).normalize == "typedesc") doAssert(($typeNode[0]).normalize == "typedesc")

View file

@ -231,6 +231,7 @@ const mimes* = {
"xcf": "application/x-xcf", "xcf": "application/x-xcf",
"fig": "application/x-xfig", "fig": "application/x-xfig",
"xpi": "application/x-xpinstall", "xpi": "application/x-xpinstall",
"wasm": "application/wasm",
"amr": "audio/amr", "amr": "audio/amr",
"awb": "audio/amr-wb", "awb": "audio/amr-wb",
"amr": "audio/amr", "amr": "audio/amr",

View file

@ -41,7 +41,7 @@
## immediately. ## immediately.
## ##
## .. code-block:: Nim ## .. 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") ## socket.sendTo("192.168.0.1", Port(27960), "status\n")
## ##
## Creating a server ## Creating a server

View file

@ -971,7 +971,7 @@ proc rawCreateDir(dir: string): bool =
elif errno in {EEXIST, ENOSYS}: elif errno in {EEXIST, ENOSYS}:
result = false result = false
else: else:
raiseOSError(osLastError()) raiseOSError(osLastError(), dir)
elif defined(posix): elif defined(posix):
let res = mkdir(dir, 0o777) let res = mkdir(dir, 0o777)
if res == 0'i32: if res == 0'i32:
@ -980,7 +980,7 @@ proc rawCreateDir(dir: string): bool =
result = false result = false
else: else:
#echo res #echo res
raiseOSError(osLastError()) raiseOSError(osLastError(), dir)
else: else:
when useWinUnicode: when useWinUnicode:
wrapUnary(res, createDirectoryW, dir) wrapUnary(res, createDirectoryW, dir)
@ -992,7 +992,7 @@ proc rawCreateDir(dir: string): bool =
elif getLastError() == 183'i32: elif getLastError() == 183'i32:
result = false result = false
else: else:
raiseOSError(osLastError()) raiseOSError(osLastError(), dir)
proc existsOrCreateDir*(dir: string): bool {.rtl, extern: "nos$1", proc existsOrCreateDir*(dir: string): bool {.rtl, extern: "nos$1",
tags: [WriteDirEffect, ReadDirEffect].} = tags: [WriteDirEffect, ReadDirEffect].} =
@ -1005,7 +1005,7 @@ proc existsOrCreateDir*(dir: string): bool {.rtl, extern: "nos$1",
if result: if result:
# path already exists - need to check that it is indeed a directory # path already exists - need to check that it is indeed a directory
if not existsDir(dir): 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", proc createDir*(dir: string) {.rtl, extern: "nos$1",
tags: [WriteDirEffect, ReadDirEffect].} = tags: [WriteDirEffect, ReadDirEffect].} =

View file

@ -147,7 +147,7 @@ else: # UNIX-like operating system
DirSep* = '/' DirSep* = '/'
AltSep* = DirSep AltSep* = DirSep
PathSep* = ':' PathSep* = ':'
FileSystemCaseSensitive* = true FileSystemCaseSensitive* = when defined(macosx): false else: true
ExeExt* = "" ExeExt* = ""
ScriptExt* = "" ScriptExt* = ""
DynlibFormat* = when defined(macosx): "lib$1.dylib" else: "lib$1.so" 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 ## | < 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: if FileSystemCaseSensitive:
result = cmp(pathA, pathB) result = cmp(pathA, pathB)
else: else:

View file

@ -110,7 +110,7 @@ proc random*[T](a: openArray[T]): T {.deprecated.} =
## Use ``rand`` instead. ## Use ``rand`` instead.
result = a[random(a.low..a.len)] 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 ## Returns a random number in the range 0..max. The sequence of
## random number is always the same, unless `randomize` is called ## random number is always the same, unless `randomize` is called
## which initializes the random number generator with a "random" ## which initializes the random number generator with a "random"
@ -128,7 +128,7 @@ proc rand*(max: int): int {.benign.} =
## number, i.e. a tickcount. ## number, i.e. a tickcount.
rand(state, max) 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 ## Returns a random number in the range 0..max. The sequence of
## random number is always the same, unless `randomize` is called ## random number is always the same, unless `randomize` is called
## which initializes the random number generator with a "random" ## which initializes the random number generator with a "random"
@ -218,4 +218,17 @@ when isMainModule:
doAssert rand(0) == 0 doAssert rand(0) == 0
doAssert rand("a") == 'a' 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() main()

View file

@ -261,6 +261,9 @@ else:
param: int param: int
data: T data: T
const
InvalidIdent = -1
proc raiseIOSelectorsError[T](message: T) = proc raiseIOSelectorsError[T](message: T) =
var msg = "" var msg = ""
when T is string: when T is string:
@ -302,6 +305,12 @@ else:
if posix.sigprocmask(SIG_UNBLOCK, newmask, oldmask) == -1: if posix.sigprocmask(SIG_UNBLOCK, newmask, oldmask) == -1:
raiseIOSelectorsError(osLastError()) raiseIOSelectorsError(osLastError())
template clearKey[T](key: ptr SelectorKey[T]) =
var empty: T
key.ident = InvalidIdent
key.events = {}
key.data = empty
when defined(linux): when defined(linux):
include ioselects/ioselectors_epoll include ioselects/ioselectors_epoll
elif bsdPlatform: elif bsdPlatform:

View file

@ -181,10 +181,27 @@ elif defined(windows):
type type
Month* = enum ## Represents a month. Note that the enum starts at ``1``, so ``ord(month)`` will give 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]``. ## 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. 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] MonthdayRange* = range[1..31]
HourRange* = range[0..23] 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) & result = intToStr(ti.hour, 2) & ':' & intToStr(ti.minute, 2) &
':' & intToStr(ti.second, 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 = proc toParts* (ti: TimeInterval): TimeIntervalParts =
## Converts a `TimeInterval` into an array consisting of its time units, ## Converts a `TimeInterval` into an array consisting of its time units,
## starting with nanoseconds and ending with years ## starting with nanoseconds and ending with years

View file

@ -1479,8 +1479,8 @@ when defined(nimdoc):
## ##
## Note that this is a *runtime* call and using ``quit`` inside a macro won't ## 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 ## have any compile time effect. If you need to stop the compiler inside a
## macro, use the `error <manual.html#error-pragma>`_ or `fatal ## macro, use the `error <manual.html#pragmas-error-pragma>`_ or `fatal
## <manual.html#fatal-pragma>`_ pragmas. ## <manual.html#pragmas-fatal-pragma>`_ pragmas.
elif defined(genode): elif defined(genode):
include genode/env 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 ## 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 ## 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 ## 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 ## .. 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 ## same as `assert` but is always turned on and not affected by the
## ``--assertions`` command line switch. ## ``--assertions`` command line switch.
bind instantiationInfo 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: if not cond:
raiseAssert(astToStr(cond) & ' ' & raiseAssert(astToStr(cond) & ' ' &
instantiationInfo(-1, false).fileName & '(' & instantiationInfo(-1, false).fileName & '(' &

View file

@ -116,6 +116,8 @@ type
nextChunkSize: int nextChunkSize: int
bottomData: AvlNode bottomData: AvlNode
heapLinks: HeapLinks heapLinks: HeapLinks
when defined(nimTypeNames):
allocCounter, deallocCounter: int
const const
fsLookupTable: array[byte, int8] = [ fsLookupTable: array[byte, int8] = [
@ -434,8 +436,9 @@ proc requestOsChunks(a: var MemRegion, size: int): PBigChunk =
a.nextChunkSize = PageSize*4 a.nextChunkSize = PageSize*4
else: else:
a.nextChunkSize = min(roundup(usedMem shr 2, PageSize), a.nextChunkSize * 2) 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: if size > a.nextChunkSize:
result = cast[PBigChunk](osAllocPages(size)) result = cast[PBigChunk](osAllocPages(size))
else: else:
@ -737,6 +740,8 @@ when false:
result = nil result = nil
proc rawAlloc(a: var MemRegion, requestedSize: int): pointer = proc rawAlloc(a: var MemRegion, requestedSize: int): pointer =
when defined(nimTypeNames):
inc(a.allocCounter)
sysAssert(allocInv(a), "rawAlloc: begin") sysAssert(allocInv(a), "rawAlloc: begin")
sysAssert(roundup(65, 8) == 72, "rawAlloc: roundup broken") sysAssert(roundup(65, 8) == 72, "rawAlloc: roundup broken")
sysAssert(requestedSize >= sizeof(FreeCell), "rawAlloc: requested size too small") sysAssert(requestedSize >= sizeof(FreeCell), "rawAlloc: requested size too small")
@ -810,6 +815,8 @@ proc rawAlloc0(a: var MemRegion, requestedSize: int): pointer =
zeroMem(result, requestedSize) zeroMem(result, requestedSize)
proc rawDealloc(a: var MemRegion, p: pointer) = proc rawDealloc(a: var MemRegion, p: pointer) =
when defined(nimTypeNames):
inc(a.deallocCounter)
#sysAssert(isAllocatedPtr(a, p), "rawDealloc: no allocated pointer") #sysAssert(isAllocatedPtr(a, p), "rawDealloc: no allocated pointer")
sysAssert(allocInv(a), "rawDealloc: begin") sysAssert(allocInv(a), "rawDealloc: begin")
var c = pageAddr(p) var c = pageAddr(p)
@ -975,6 +982,10 @@ proc getOccupiedMem(a: MemRegion): int {.inline.} =
result = a.occ result = a.occ
# a.currMem - a.freeMem # a.currMem - a.freeMem
when defined(nimTypeNames):
proc getMemCounters(a: MemRegion): (int, int) {.inline.} =
(a.allocCounter, a.deallocCounter)
# ---------------------- thread memory region ------------------------------- # ---------------------- thread memory region -------------------------------
template instantiateForRegion(allocator: untyped) = template instantiateForRegion(allocator: untyped) =
@ -1018,6 +1029,9 @@ template instantiateForRegion(allocator: untyped) =
proc getOccupiedMem(): int = return allocator.occ #getTotalMem() - getFreeMem() proc getOccupiedMem(): int = return allocator.occ #getTotalMem() - getFreeMem()
proc getMaxMem*(): int = return getMaxMem(allocator) proc getMaxMem*(): int = return getMaxMem(allocator)
when defined(nimTypeNames):
proc getMemCounters*(): (int, int) = getMemCounters(allocator)
# -------------------- shared heap region ---------------------------------- # -------------------- shared heap region ----------------------------------
when hasThreadSupport: when hasThreadSupport:
var sharedHeap: MemRegion var sharedHeap: MemRegion

View file

@ -27,6 +27,9 @@ proc c_strcmp(a, b: cstring): cint {.
importc: "strcmp", header: "<string.h>", noSideEffect.} importc: "strcmp", header: "<string.h>", noSideEffect.}
proc c_strlen(a: cstring): csize {. proc c_strlen(a: cstring): csize {.
importc: "strlen", header: "<string.h>", noSideEffect.} importc: "strlen", header: "<string.h>", noSideEffect.}
proc c_abort() {.
importc: "abort", header: "<stdlib.h>", noSideEffect.}
when defined(linux) and defined(amd64): when defined(linux) and defined(amd64):
type type

View file

@ -548,7 +548,10 @@ proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer =
gcTrace(res, csAllocated) gcTrace(res, csAllocated)
track("growObj old", ol, 0) track("growObj old", ol, 0)
track("growObj new", res, newsize) 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") sysAssert(allocInv(gch.region), "growObj before dealloc")
if ol.refcount shr rcShift <=% 1: if ol.refcount shr rcShift <=% 1:
# free immediately to save space: # free immediately to save space:

View file

@ -57,6 +57,9 @@ when defined(nimTypeNames):
for i in 0 .. n-1: 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] %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) 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): when defined(nimGcRefLeak):
proc oomhandler() = proc oomhandler() =

View file

@ -417,12 +417,18 @@ proc setStdIoUnbuffered() =
discard c_setvbuf(stdin, nil, IONBF, 0) discard c_setvbuf(stdin, nil, IONBF, 0)
when declared(stdout): when declared(stdout):
when defined(windows) and compileOption("threads"):
var echoLock: SysLock
initSysLock echoLock
proc echoBinSafe(args: openArray[string]) {.compilerProc.} = proc echoBinSafe(args: openArray[string]) {.compilerProc.} =
# flockfile deadlocks some versions of Android 5.x.x # flockfile deadlocks some versions of Android 5.x.x
when not defined(windows) and not defined(android) and not defined(nintendoswitch): when not defined(windows) and not defined(android) and not defined(nintendoswitch):
proc flockfile(f: File) {.importc, noDecl.} proc flockfile(f: File) {.importc, noDecl.}
proc funlockfile(f: File) {.importc, noDecl.} proc funlockfile(f: File) {.importc, noDecl.}
flockfile(stdout) flockfile(stdout)
when defined(windows) and compileOption("threads"):
acquireSys echoLock
for s in args: for s in args:
discard c_fwrite(s.cstring, s.len, 1, stdout) discard c_fwrite(s.cstring, s.len, 1, stdout)
const linefeed = "\n" # can be 1 or more chars const linefeed = "\n" # can be 1 or more chars
@ -430,5 +436,7 @@ when declared(stdout):
discard c_fflush(stdout) discard c_fflush(stdout)
when not defined(windows) and not defined(android) and not defined(nintendoswitch): when not defined(windows) and not defined(android) and not defined(nintendoswitch):
funlockfile(stdout) funlockfile(stdout)
when defined(windows) and compileOption("threads"):
releaseSys echoLock
{.pop.} {.pop.}

View file

@ -144,8 +144,13 @@ proc addChar(s: NimString, c: char): NimString =
result = s result = s
if result.len >= result.space: if result.len >= result.space:
let r = resize(result.space) let r = resize(result.space)
result = cast[NimString](growObj(result, when defined(nimIncrSeqV3):
sizeof(TGenericSeq) + r + 1)) 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.reserved = r
result.data[result.len] = c result.data[result.len] = c
result.data[result.len+1] = '\0' result.data[result.len+1] = '\0'
@ -188,8 +193,13 @@ proc resizeString(dest: NimString, addlen: int): NimString {.compilerRtl.} =
elif dest.len + addlen <= dest.space: elif dest.len + addlen <= dest.space:
result = dest result = dest
else: # slow path: else: # slow path:
var sp = max(resize(dest.space), dest.len + addlen) let sp = max(resize(dest.space), dest.len + addlen)
result = cast[NimString](growObj(dest, sizeof(TGenericSeq) + sp + 1)) 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.reserved = sp
#result = rawNewString(sp) #result = rawNewString(sp)
#copyMem(result, dest, dest.len + sizeof(TGenericSeq)) #copyMem(result, dest, dest.len + sizeof(TGenericSeq))
@ -212,7 +222,15 @@ proc setLengthStr(s: NimString, newLen: int): NimString {.compilerRtl.} =
elif n <= s.space: elif n <= s.space:
result = s result = s
else: 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.len = n
result.data[n] = '\0' result.data[n] = '\0'
@ -242,6 +260,9 @@ proc incrSeqV2(seq: PGenericSeq, elemSize: int): PGenericSeq {.compilerProc.} =
GenericSeqSize)) GenericSeqSize))
result.reserved = r result.reserved = r
template `+!`(p: pointer, s: int): pointer =
cast[pointer](cast[int](p) +% s)
proc incrSeqV3(s: PGenericSeq, typ: PNimType): PGenericSeq {.compilerProc.} = proc incrSeqV3(s: PGenericSeq, typ: PNimType): PGenericSeq {.compilerProc.} =
if s == nil: if s == nil:
result = cast[PGenericSeq](newSeq(typ, 1)) result = cast[PGenericSeq](newSeq(typ, 1))
@ -250,9 +271,16 @@ proc incrSeqV3(s: PGenericSeq, typ: PNimType): PGenericSeq {.compilerProc.} =
result = s result = s
if result.len >= result.space: if result.len >= result.space:
let r = resize(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)) GenericSeqSize))
result.reserved = r result.reserved = r
proc setLengthSeq(seq: PGenericSeq, elemSize, newLen: int): PGenericSeq {. proc setLengthSeq(seq: PGenericSeq, elemSize, newLen: int): PGenericSeq {.
compilerRtl, inl.} = compilerRtl, inl.} =
@ -296,7 +324,40 @@ proc setLengthSeq(seq: PGenericSeq, elemSize, newLen: int): PGenericSeq {.
proc setLengthSeqV2(s: PGenericSeq, typ: PNimType, newLen: int): PGenericSeq {. proc setLengthSeqV2(s: PGenericSeq, typ: PNimType, newLen: int): PGenericSeq {.
compilerRtl.} = compilerRtl.} =
sysAssert typ.kind == tySequence, "setLengthSeqV2: type is not a seq"
if s == nil: if s == nil:
result = cast[PGenericSeq](newSeq(typ, newLen)) result = cast[PGenericSeq](newSeq(typ, newLen))
else: 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)

View file

@ -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()

View file

@ -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())

View file

@ -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"

View file

@ -15,7 +15,7 @@ proc main =
let val = s[i]() let val = s[i]()
if val != $(i*i): echo "bug ", val 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" echo "success"
main() main()

16
tests/errmsgs/t8434.nim Normal file
View file

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

View file

@ -16,9 +16,12 @@ proc main =
dealloc p dealloc p
# c_fprintf(stdout, "iteration: %ld size: %ld\n", i, size) # c_fprintf(stdout, "iteration: %ld size: %ld\n", i, size)
when defined(cpu64): when defined(cpu64):
# bug #7120 # see https://github.com/nim-lang/Nim/issues/8509
var x = alloc(((1 shl 29) - 4) * 8) # this often made appveyor (on windows) fail with out of memory
dealloc x when defined(posix):
# bug #7120
var x = alloc(((1 shl 29) - 4) * 8)
dealloc x
main() main()

View file

@ -14,7 +14,7 @@ proc handleRequest(query: string): StringTableRef =
let x = foo let x = foo
result = x() result = x()
const Limit = when compileOption("gc", "markAndSweep"): 5*1024*1024 else: 700_000 const Limit = 5*1024*1024
proc main = proc main =
var counter = 0 var counter = 0

11
tests/generics/t8403.nim Normal file
View file

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

10
tests/generics/t8439.nim Normal file
View file

@ -0,0 +1,10 @@
discard """
output: "1"
"""
type
Cardinal = enum
north, east, south, west
proc foo[cardinal: static[Cardinal]](): int = 1
echo(foo[north]())

20
tests/iter/t338.nim Normal file
View file

@ -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()

View file

@ -1,4 +1,9 @@
discard """ discard """
msg: '''initApple
deinitApple
Coral
enum
redCoral, blackCoral'''
output: '''TFoo output: '''TFoo
TBar''' TBar'''
""" """
@ -23,3 +28,40 @@ macro test: untyped =
bindSym("TBar")) bindSym("TBar"))
test() 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()

View file

@ -43,4 +43,35 @@ macro repr_and_parse(fn: typed): typed =
echo fn_impl.repr echo fn_impl.repr
result = parseStmt(fn_impl.repr) result = parseStmt(fn_impl.repr)
repr_and_parse(f) 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)

View file

@ -1,5 +1,7 @@
# Test the sizeof proc discard """
file: "tsize.nim"
output: "40 3 12 32"
"""
type type
TMyRecord {.final.} = object TMyRecord {.final.} = object
x, y: int x, y: int
@ -7,4 +9,20 @@ type
r: float r: float
s: string 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)) write(stdout, sizeof(TMyRecord))
echo ' ', mysize1, ' ', mysize2, ' ',mysize3

View file

@ -154,3 +154,23 @@ block:
let a: proc(x: int) {.defaultValue(5).} = nil let a: proc(x: int) {.defaultValue(5).} = nil
static: static:
doAssert hasCustomPragma(a.type, defaultValue) 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

22
tests/sets/tsetpop.nim Normal file
View file

@ -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

View file

@ -205,4 +205,12 @@ echo warnUninit in gNotes
# 7555 # 7555
doAssert {-1.int8, -2, -2}.card == 2 doAssert {-1.int8, -2, -2}.card == 2
doAssert {1, 2, 2, 3..5, 4..6}.card == 6 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

View file

@ -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"

View file

@ -337,7 +337,7 @@ when isMainModule:
n2: Option[int] n2: Option[int]
n3: Option[string] n3: Option[string]
n4: Option[bool] n4: Option[bool]
var j0 = parseJson("""{"n1": 1, "n2": null, "n3": null, "n4": null}""") var j0 = parseJson("""{"n1": 1, "n2": null, "n3": null, "n4": null}""")
let j0Deser = j0.to(Obj) let j0Deser = j0.to(Obj)
doAssert j0Deser.n1 == 1 doAssert j0Deser.n1 == 1
@ -411,10 +411,109 @@ when isMainModule:
doAssert dataDeser.a == 1 doAssert dataDeser.a == 1
doAssert dataDeser.f == 6 doAssert dataDeser.f == 6
doAssert dataDeser.i == 9.9'f32 doAssert dataDeser.i == 9.9'f32
# deserialize directly into a table # deserialize directly into a table
block: block:
let s = """{"a": 1, "b": 2}""" let s = """{"a": 1, "b": 2}"""
let t = parseJson(s).to(Table[string, int]) let t = parseJson(s).to(Table[string, int])
doAssert t["a"] == 1 doAssert t["a"] == 1
doAssert t["b"] == 2 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"

View file

@ -145,7 +145,7 @@ else:
echo getLastModificationTime("a") == tm echo getLastModificationTime("a") == tm
removeFile("a") removeFile("a")
when defined(Linux) or defined(macosx): when defined(posix):
block normalizedPath: block normalizedPath:
block relative: block relative:

18
tests/system/t7894.nim Normal file
View file

@ -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

View file

@ -1,54 +1,57 @@
discard """
"""
var x: ptr int var x: ptr int
x = cast[ptr int](alloc(7)) x = cast[ptr int](alloc(7))
assert x != nil doAssert x != nil
x = cast[ptr int](x.realloc(2)) x = cast[ptr int](x.realloc(2))
assert x != nil doAssert x != nil
x.dealloc() x.dealloc()
x = createU(int, 3) x = createU(int, 3)
assert x != nil doAssert x != nil
x.dealloc() x.dealloc()
x = create(int, 4) x = create(int, 4)
assert cast[ptr array[4, int]](x)[0] == 0 doAssert cast[ptr array[4, int]](x)[0] == 0
assert cast[ptr array[4, int]](x)[1] == 0 doAssert cast[ptr array[4, int]](x)[1] == 0
assert cast[ptr array[4, int]](x)[2] == 0 doAssert cast[ptr array[4, int]](x)[2] == 0
assert cast[ptr array[4, int]](x)[3] == 0 doAssert cast[ptr array[4, int]](x)[3] == 0
x = x.resize(4) x = x.resize(4)
assert x != nil doAssert x != nil
x.dealloc() x.dealloc()
x = cast[ptr int](allocShared(100)) x = cast[ptr int](allocShared(100))
assert x != nil doAssert x != nil
deallocShared(x) deallocShared(x)
x = createSharedU(int, 3) x = createSharedU(int, 3)
assert x != nil doAssert x != nil
x.deallocShared() x.deallocShared()
x = createShared(int, 3) x = createShared(int, 3)
assert x != nil doAssert x != nil
assert cast[ptr array[3, int]](x)[0] == 0 doAssert cast[ptr array[3, int]](x)[0] == 0
assert cast[ptr array[3, int]](x)[1] == 0 doAssert cast[ptr array[3, int]](x)[1] == 0
assert cast[ptr array[3, int]](x)[2] == 0 doAssert cast[ptr array[3, int]](x)[2] == 0
assert x != nil doAssert x != nil
x = cast[ptr int](x.resizeShared(2)) x = cast[ptr int](x.resizeShared(2))
assert x != nil doAssert x != nil
x.deallocShared() x.deallocShared()
x = create(int, 10) x = create(int, 10)
assert x != nil doAssert x != nil
x = x.resize(12) x = x.resize(12)
assert x != nil doAssert x != nil
x.dealloc() x.dealloc()
x = createShared(int, 1) x = createShared(int, 1)
assert x != nil doAssert x != nil
x = x.resizeShared(1) x = x.resizeShared(1)
assert x != nil doAssert x != nil
x.deallocShared() x.deallocShared()
x = cast[ptr int](alloc0(125 shl 23)) x = cast[ptr int](alloc0(125 shl 23))

View file

@ -1,3 +1,6 @@
discard """
"""
const const
nmax = 2*1024*1024*1024 nmax = 2*1024*1024*1024

View file

@ -65,7 +65,7 @@ proc main() =
for val in table.values(): for val in table.values():
if myObj2.isNil: if myObj2.isNil:
myObj2 = val myObj2 = val
assert(myObj == myObj2) # passes doAssert(myObj == myObj2) # passes
var tableCopy: ListTableRef[int, SomeObj] var tableCopy: ListTableRef[int, SomeObj]
deepCopy(tableCopy, table) deepCopy(tableCopy, table)
@ -80,7 +80,7 @@ proc main() =
#echo cast[int](myObjCopy) #echo cast[int](myObjCopy)
#echo cast[int](myObjCopy2) #echo cast[int](myObjCopy2)
assert(myObjCopy == myObjCopy2) # fails doAssert(myObjCopy == myObjCopy2) # fails
type type
@ -88,7 +88,7 @@ type
counter, max: int counter, max: int
data: array[0..99, (pointer, pointer)] 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() main()
echo "ok" echo "ok"

View file

@ -1,3 +1,6 @@
discard """
"""
import import
unittest, osproc, streams, os, strformat 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." 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}") discard execProcess(&"dd if=/dev/zero of={fn} bs=1000000 count={size_in_mb}")
doAssert os.getFileSize(fn) == sz # Verify OS filesize by string doAssert os.getFileSize(fn) == sz # Verify OS filesize by string
var f = open(fn) var f = open(fn)
doAssert f.getFileSize() == sz # Verify file handle filesize doAssert f.getFileSize() == sz # Verify file handle filesize
f.close() f.close()

View file

@ -1,3 +1,6 @@
discard """
"""
import os import os
import osproc import osproc
import parseopt2 import parseopt2
@ -7,12 +10,12 @@ let argv = commandLineParams()
if argv == @[]: if argv == @[]:
# this won't work with spaces # 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: else:
let f = toSeq(getopt()) let f = toSeq(getopt())
echo f.repr echo f.repr
assert f[0].kind == cmdArgument and f[0].key == "foo bar" and f[0].val == "" doAssert 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" doAssert 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" doAssert 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 == "" doAssert 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[4].kind == cmdShortOption and f[4].key == "c" and f[4].val == ""

View file

@ -54,4 +54,9 @@ static:
new(s) new(s)
var ss = s var ss = s
s[] = 1 s[] = 1
doAssert ss[] == 1 doAssert ss[] == 1
static: # bug #8402
type R = ref object
var empty: R
let otherEmpty = empty

View file

@ -1,6 +1,5 @@
import karax import karax
import fuzzysearch
proc findNodeWith(x: Element; tag, content: cstring): Element = proc findNodeWith(x: Element; tag, content: cstring): Element =
if x.nodeName == tag and x.textContent == content: 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 ul.len != 0: result.add ul
if result.len == 0: result = nil if result.len == 0: result = nil
proc containsWord(a, b: cstring): bool {.asmNoStackFrame.} = #proc containsWord(a, b: cstring): bool {.asmNoStackFrame.} =
{.emit: """ #{.emit: """
var escaped = `b`.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); #var escaped = `b`.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
return new RegExp("\\b" + escaped + "\\b").test(`a`); #return new RegExp("\\b" + escaped + "\\b").test(`a`);
""".} #""".}
proc isWhitespace(text: cstring): bool {.asmNoStackFrame.} = proc isWhitespace(text: cstring): bool {.asmNoStackFrame.} =
{.emit: """ {.emit: """
@ -252,24 +251,29 @@ proc dosearch(value: cstring): Element =
`stuff` = doc.documentElement; `stuff` = doc.documentElement;
""".} """.}
db = stuff.getElementsByClass"reference external" db = stuff.getElementsByClass"reference"
contents = @[] contents = @[]
for ahref in db: for ahref in db:
contents.add ahref.textContent.normalize contents.add ahref.getAttribute("data-doc-search-tag")
let ul = tree("UL") let ul = tree("UL")
result = tree("DIV") result = tree("DIV")
result.setClass"search_results" result.setClass"search_results"
var matches: seq[(Element, int)] = @[] var matches: seq[(Element, int)] = @[]
let key = value.normalize
for i in 0..<db.len: for i in 0..<db.len:
let c = contents[i] let c = contents[i]
if c.containsWord(key): if c == "Examples" or c == "PEG construction":
matches.add((db[i], -(30_000 - c.len))) # Some manual exclusions.
elif c.contains(key): # Ideally these should be fixed in the index to be more
matches.add((db[i], c.len)) # 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: matches.sort do (a, b: auto) -> int:
a[1] - b[1] b[1] - a[1]
for i in 0..min(<matches.len, 19): 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])) ul.add(tree("LI", matches[i][0]))
if ul.len == 0: if ul.len == 0:
result.add tree("B", text"no search results") result.add tree("B", text"no search results")

View file

@ -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),
)

View file

@ -13,16 +13,18 @@ import
from xmltree import escape from xmltree import escape
const gitRepo = "https://github.com/nim-lang/Nim"
type type
TKeyValPair = tuple[key, id, val: string] TKeyValPair = tuple[key, id, val: string]
TConfigData = object of RootObj TConfigData = object of RootObj
tabs, links: seq[TKeyValPair] tabs, links: seq[TKeyValPair]
doc, srcdoc, srcdoc2, webdoc, pdf: seq[string] doc, srcdoc, srcdoc2, webdoc, pdf: seq[string]
authors, projectName, projectTitle, logo, infile, outdir, ticker: string authors, projectName, projectTitle, logo, infile, ticker: string
vars: StringTableRef vars: StringTableRef
nimCompiler: string
nimArgs: string nimArgs: string
gitURL: string
docHTMLOutput: string
webUploadOutput: string
quotations: Table[string, tuple[quote, author: string]] quotations: Table[string, tuple[quote, author: string]]
numProcessors: int # Set by parallelBuild:n, only works for values > 0. numProcessors: int # Set by parallelBuild:n, only works for values > 0.
gaId: string # google analytics ID, nil means analytics are disabled gaId: string # google analytics ID, nil means analytics are disabled
@ -51,8 +53,10 @@ proc initConfigData(c: var TConfigData) =
c.webdoc = @[] c.webdoc = @[]
c.pdf = @[] c.pdf = @[]
c.infile = "" c.infile = ""
c.outdir = ""
c.nimArgs = "--hint[Conf]:off --hint[Path]:off --hint[Processing]:off -d:boot " 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.authors = ""
c.projectTitle = "" c.projectTitle = ""
c.projectName = "" c.projectName = ""
@ -79,14 +83,19 @@ const
Usage: Usage:
nimweb [options] ini-file[.ini] [compile_options] nimweb [options] ini-file[.ini] [compile_options]
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 -h, --help shows this help
-v, --version shows the version -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 --website only build the website, not the full documentation
--pdf build the PDF version of the documentation --pdf build the PDF version of the documentation
--json2 build JSON of the documentation --json2 build JSON of the documentation
--onlyDocs build only 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: Compile_options:
will be passed to the Nim compiler will be passed to the Nim compiler
""" """
@ -145,7 +154,11 @@ proc parseCmdLine(c: var TConfigData) =
of "version", "v": of "version", "v":
stdout.write(version & "\n") stdout.write(version & "\n")
quit(0) 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": of "parallelbuild":
try: try:
let num = parseInt(val) let num = parseInt(val)
@ -163,8 +176,14 @@ proc parseCmdLine(c: var TConfigData) =
of "googleanalytics": of "googleanalytics":
c.gaId = val c.gaId = val
c.nimArgs.add("--doc.googleAnalytics:" & 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: else:
echo("Invalid argument $1" % [key]) echo("Invalid argument '$1'" % [key])
quit(usage) quit(usage)
of cmdEnd: break of cmdEnd: break
if c.infile.len == 0: quit(usage) if c.infile.len == 0: quit(usage)
@ -244,15 +263,14 @@ proc parseIniFile(c: var TConfigData) =
close(p) close(p)
if c.projectName.len == 0: if c.projectName.len == 0:
c.projectName = changeFileExt(extractFilename(c.infile), "") c.projectName = changeFileExt(extractFilename(c.infile), "")
if c.outdir.len == 0:
c.outdir = splitFile(c.infile).dir
# ------------------- main ---------------------------------------------------- # ------------------- main ----------------------------------------------------
proc exe(f: string): string = return addFileExt(f, ExeExt) 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 var nim = "nim".exe
result = "bin" / nim result = "bin" / nim
if existsFile(result): return 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 ## it didn't make much sense to integrate into the existing generic
## documentation builders. ## documentation builders.
const src = "doc"/"docgen_sample.nim" const src = "doc"/"docgen_sample.nim"
exec(findNim() & " doc $# -o:$# $#" % exec(findNim(c) & " doc $# -o:$# $#" %
[c.nimArgs, destPath / "docgen_sample.html", src]) [c.nimArgs, destPath / "docgen_sample.html", src])
exec(findNim() & " doc2 $# -o:$# $#" % exec(findNim(c) & " doc2 $# -o:$# $#" %
[c.nimArgs, destPath / "docgen_sample2.html", src]) [c.nimArgs, destPath / "docgen_sample2.html", src])
proc pathPart(d: string): string = splitFile(d).dir.replace('\\', '/') 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)) commands = newSeq[string](len(c.doc) + len(c.srcdoc) + len(c.srcdoc2))
i = 0 i = 0
for d in items(c.doc): for d in items(c.doc):
commands[i] = findNim() & " rst2html $# --git.url:$# -o:$# --index:on $#" % commands[i] = findNim(c) & " rst2html $# --git.url:$# -o:$# --index:on $#" %
[c.nimArgs, gitRepo, [c.nimArgs, c.gitURL,
destPath / changeFileExt(splitFile(d).name, "html"), d] destPath / changeFileExt(splitFile(d).name, "html"), d]
i.inc i.inc
for d in items(c.srcdoc): for d in items(c.srcdoc):
commands[i] = findNim() & " doc0 $# --git.url:$# -o:$# --index:on $#" % commands[i] = findNim(c) & " doc0 $# --git.url:$# -o:$# --index:on $#" %
[c.nimArgs, gitRepo, [c.nimArgs, c.gitURL,
destPath / changeFileExt(splitFile(d).name, "html"), d] destPath / changeFileExt(splitFile(d).name, "html"), d]
i.inc i.inc
for d in items(c.srcdoc2): for d in items(c.srcdoc2):
commands[i] = findNim() & " doc2 $# --git.url:$# -o:$# --index:on $#" % commands[i] = findNim(c) & " doc2 $# --git.url:$# -o:$# --index:on $#" %
[c.nimArgs, gitRepo, [c.nimArgs, c.gitURL,
destPath / changeFileExt(splitFile(d).name, "html"), d] destPath / changeFileExt(splitFile(d).name, "html"), d]
i.inc i.inc
mexec(commands, c.numProcessors) 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) = proc buildPdfDoc(c: var TConfigData, destPath: string) =
createDir(destPath) createDir(destPath)
@ -327,7 +345,7 @@ proc buildPdfDoc(c: var TConfigData, destPath: string) =
else: else:
const pdflatexcmd = "pdflatex -interaction=nonstopmode " const pdflatexcmd = "pdflatex -interaction=nonstopmode "
for d in items(c.pdf): 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: # call LaTeX twice to get cross references right:
exec(pdflatexcmd & changeFileExt(d, "tex")) exec(pdflatexcmd & changeFileExt(d, "tex"))
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): # build additional documentation (without the index):
var commands = newSeq[string](c.webdoc.len) var commands = newSeq[string](c.webdoc.len)
for i, doc in pairs(c.webdoc): for i, doc in pairs(c.webdoc):
commands[i] = findNim() & " doc2 $# --git.url:$# -o:$# $#" % commands[i] = findNim(c) & " doc2 $# --git.url:$# -o:$# $#" %
[c.nimArgs, gitRepo, [c.nimArgs, c.gitURL,
destPath / changeFileExt(splitFile(doc).name, "html"), doc] destPath / changeFileExt(splitFile(doc).name, "html"), doc]
mexec(commands, c.numProcessors) mexec(commands, c.numProcessors)
@ -431,9 +449,9 @@ proc buildNewsRss(c: var TConfigData, destPath: string) =
generateRss(destFilename, parseNewsTitles(srcFilename)) generateRss(destFilename, parseNewsTitles(srcFilename))
proc buildJS(destPath: string) = proc buildJS(c: TConfigData) =
exec(findNim() & " js -d:release --out:$1 web/nimblepkglist.nim" % exec(findNim(c) & " js -d:release --out:$1 web/nimblepkglist.nim" %
[destPath / "nimblepkglist.js"]) [c.webUploadOutput / "nimblepkglist.js"])
proc readSponsors(sponsorsFile: string): seq[Sponsor] = proc readSponsors(sponsorsFile: string): seq[Sponsor] =
result = @[] result = @[]
@ -464,7 +482,7 @@ const
cmdRst2Html = " rst2html --compileonly $1 -o:web/$2.temp web/$2.rst" cmdRst2Html = " rst2html --compileonly $1 -o:web/$2.temp web/$2.rst"
proc buildPage(c: var TConfigData, file, title, rss: string, assetDir = "") = 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 temp = "web" / changeFileExt(file, "temp")
var content: string var content: string
try: try:
@ -472,7 +490,7 @@ proc buildPage(c: var TConfigData, file, title, rss: string, assetDir = "") =
except IOError: except IOError:
quit("[Error] cannot open: " & temp) quit("[Error] cannot open: " & temp)
var f: File var f: File
var outfile = "web/upload/$#.html" % file var outfile = c.webUploadOutput / "$#.html" % file
if not existsDir(outfile.splitFile.dir): if not existsDir(outfile.splitFile.dir):
createDir(outfile.splitFile.dir) createDir(outfile.splitFile.dir)
if open(f, outfile, fmWrite): if open(f, outfile, fmWrite):
@ -502,27 +520,25 @@ proc buildWebsite(c: var TConfigData) =
let rss = if file in ["news", "index"]: extractFilename(rssUrl) else: "" let rss = if file in ["news", "index"]: extractFilename(rssUrl) else: ""
if '.' in file: continue if '.' in file: continue
buildPage(c, file, if file == "question": "FAQ" else: file, rss) buildPage(c, file, if file == "question": "FAQ" else: file, rss)
copyDir("web/assets", "web/upload/assets") copyDir("web/assets", c.webUploadOutput / "assets")
buildNewsRss(c, "web/upload") buildNewsRss(c, c.webUploadOutput)
buildSponsors(c, "web/upload") buildSponsors(c, c.webUploadOutput)
buildNews(c, "web/news", "web/upload/news") 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) = proc main(c: var TConfigData) =
buildWebsite(c) buildWebsite(c)
buildJS("web/upload") buildJS(c)
const docup = "web/upload/" & NimVersion let docup = c.webUploadOutput / NimVersion
createDir(docup) createDir(docup)
buildAddDoc(c, docup) buildAddDoc(c, docup)
buildDocSamples(c, docup) buildDocSamples(c, docup)
buildDoc(c, docup) buildDoc(c, docup)
createDir("doc/html") onlyDocs(c)
buildDocSamples(c, "doc/html")
buildDoc(c, "doc/html")
proc onlyDocs(c: var TConfigData) =
createDir("doc/html")
buildDocSamples(c, "doc/html")
buildDoc(c, "doc/html")
proc json2(c: var TConfigData) = proc json2(c: var TConfigData) =
const destPath = "web/json2" const destPath = "web/json2"
@ -530,8 +546,8 @@ proc json2(c: var TConfigData) =
var i = 0 var i = 0
for d in items(c.srcdoc2): for d in items(c.srcdoc2):
createDir(destPath / splitFile(d).dir) createDir(destPath / splitFile(d).dir)
commands[i] = findNim() & " jsondoc2 $# --git.url:$# -o:$# --index:on $#" % commands[i] = findNim(c) & " jsondoc2 $# --git.url:$# -o:$# --index:on $#" %
[c.nimArgs, gitRepo, [c.nimArgs, c.gitURL,
destPath / changeFileExt(d, "json"), d] destPath / changeFileExt(d, "json"), d]
i.inc i.inc