make at least bootstrapping work

This commit is contained in:
Araq
2018-08-05 14:17:24 +02:00
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/
test.txt
/test.ini

View File

@@ -22,7 +22,8 @@
become an error in the future.
- The ``'c`` and ``'C'`` prefix for octal literals is now deprecated to
bring the language in line with the standard library (e.g. ``parseOct``).
- The dot style for import paths (e.g ``import path.to.module`` instead of
``import path/to/module``) has been deprecated.
#### Breaking changes in the standard library
@@ -59,6 +60,8 @@
1-based coordinates on POSIX for correct behaviour; the Windows behaviour
was always correct).
- ``lineInfoObj`` now returns absolute path instead of project path.
It's used by ``lineInfo``, ``check``, ``expect``, ``require``, etc.
#### Breaking changes in the compiler
@@ -193,4 +196,9 @@
- Nintendo Switch was added as a new platform target. See [the compiler user guide](https://nim-lang.org/docs/nimc.html)
for more info.
- macros.bindSym now capable to accepts not only literal string or string constant expression.
bindSym enhancement make it also can accepts computed string or ident node inside macros /
compile time functions / static blocks. Only in templates / regular code it retains it's old behavior.
This new feature can be accessed via {.experimental: "dynamicBindSym".} pragma/switch
### Bugfixes

View File

@@ -1085,7 +1085,7 @@ proc genStrAppend(p: BProc, e: PNode, d: var TLoc) =
# appendChar(s, 'z');
# }
var
a, dest: TLoc
a, dest, call: TLoc
appends, lens: Rope
assert(d.k == locNone)
var L = 0
@@ -1109,8 +1109,9 @@ proc genStrAppend(p: BProc, e: PNode, d: var TLoc) =
linefmt(p, cpsStmts, "#prepareAdd($1, $2$3);$n",
addrLoc(p.config, dest), lens, rope(L))
else:
linefmt(p, cpsStmts, "$1 = #resizeString($1, $2$3);$n",
rdLoc(dest), lens, rope(L))
initLoc(call, locCall, e, OnHeap)
call.r = ropecg(p.module, "#resizeString($1, $2$3)", [rdLoc(dest), lens, rope(L)])
genAssignment(p, dest, call, {})
add(p.s(cpsStmts), appends)
gcUsage(p.config, e)
@@ -1119,17 +1120,20 @@ proc genSeqElemAppend(p: BProc, e: PNode, d: var TLoc) =
# seq = (typeof seq) incrSeq(&seq->Sup, sizeof(x));
# seq->data[seq->len-1] = x;
let seqAppendPattern = if not p.module.compileToCpp:
"$1 = ($2) #incrSeqV3(&($1)->Sup, $3);$n"
"($2) #incrSeqV3(&($1)->Sup, $3)"
else:
"$1 = ($2) #incrSeqV3($1, $3);$n"
var a, b, dest, tmpL: TLoc
"($2) #incrSeqV3($1, $3)"
var a, b, dest, tmpL, call: TLoc
initLocExpr(p, e.sons[1], a)
initLocExpr(p, e.sons[2], b)
let seqType = skipTypes(e.sons[1].typ, {tyVar})
lineCg(p, cpsStmts, seqAppendPattern, [
rdLoc(a),
getTypeDesc(p.module, e.sons[1].typ),
genTypeInfo(p.module, seqType, e.info)])
initLoc(call, locCall, e, OnHeap)
call.r = ropecg(p.module, seqAppendPattern, [rdLoc(a),
getTypeDesc(p.module, e.sons[1].typ),
genTypeInfo(p.module, seqType, e.info)])
# emit the write barrier if required, but we can always move here, so
# use 'genRefAssign' for the seq.
genRefAssign(p, a, call, {})
#if bt != b.t:
# echo "YES ", e.info, " new: ", typeToString(bt), " old: ", typeToString(b.t)
initLoc(dest, locExpr, e.sons[2], OnHeap)
@@ -1536,7 +1540,7 @@ proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc) =
if p.config.selectedGc == gcDestructors:
genCall(p, e, d)
return
var a, b: TLoc
var a, b, call: TLoc
assert(d.k == locNone)
var x = e.sons[1]
if x.kind in {nkAddr, nkHiddenAddr}: x = x[0]
@@ -1544,20 +1548,30 @@ proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc) =
initLocExpr(p, e.sons[2], b)
let t = skipTypes(e.sons[1].typ, {tyVar})
let setLenPattern = if not p.module.compileToCpp:
"$1 = ($3) #setLengthSeqV2(&($1)->Sup, $4, $2);$n"
"($3) #setLengthSeqV2(&($1)->Sup, $4, $2)"
else:
"$1 = ($3) #setLengthSeqV2($1, $4, $2);$n"
"($3) #setLengthSeqV2($1, $4, $2)"
lineCg(p, cpsStmts, setLenPattern, [
initLoc(call, locCall, e, OnHeap)
call.r = ropecg(p.module, setLenPattern, [
rdLoc(a), rdLoc(b), getTypeDesc(p.module, t),
genTypeInfo(p.module, t.skipTypes(abstractInst), e.info)])
genAssignment(p, a, call, {})
gcUsage(p.config, e)
proc genSetLengthStr(p: BProc, e: PNode, d: var TLoc) =
if p.config.selectedGc == gcDestructors:
binaryStmtAddr(p, e, d, "#setLengthStrV2($1, $2);$n")
else:
binaryStmt(p, e, d, "$1 = #setLengthStr($1, $2);$n")
var a, b, call: TLoc
if d.k != locNone: internalError(p.config, e.info, "genSetLengthStr")
initLocExpr(p, e.sons[1], a)
initLocExpr(p, e.sons[2], b)
initLoc(call, locCall, e, OnHeap)
call.r = ropecg(p.module, "#setLengthStr($1, $2)", [
rdLoc(a), rdLoc(b)])
genAssignment(p, a, call, {})
gcUsage(p.config, e)
proc genSwap(p: BProc, e: PNode, d: var TLoc) =
@@ -1878,7 +1892,12 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
if p.config.selectedGC == gcDestructors:
binaryStmtAddr(p, e, d, "#nimAddCharV1($1, $2);$n")
else:
binaryStmt(p, e, d, "$1 = #addChar($1, $2);$n")
var dest, b, call: TLoc
initLoc(call, locCall, e, OnHeap)
initLocExpr(p, e.sons[1], dest)
initLocExpr(p, e.sons[2], b)
call.r = ropecg(p.module, "#addChar($1, $2)", [rdLoc(dest), rdLoc(b)])
genAssignment(p, dest, call, {})
of mAppendStrStr: genStrAppend(p, e, d)
of mAppendSeqElem:
if p.config.selectedGc == gcDestructors:

View File

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

View File

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

View File

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

View File

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

View File

@@ -222,7 +222,7 @@ proc interestingIterVar(s: PSym): bool {.inline.} =
# XXX optimization: Only lift the variable if it lives across
# yield/return boundaries! This can potentially speed up
# closure iterators quite a bit.
result = s.kind in {skVar, skLet, skTemp, skForVar} and sfGlobal notin s.flags
result = s.kind in {skResult, skVar, skLet, skTemp, skForVar} and sfGlobal notin s.flags
template isIterator*(owner: PSym): bool =
owner.kind == skIterator and owner.typ.callConv == ccClosure
@@ -458,6 +458,10 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
of nkLambdaKinds, nkIteratorDef, nkFuncDef:
if n.typ != nil:
detectCapturedVars(n[namePos], owner, c)
of nkReturnStmt:
if n[0].kind in {nkAsgn, nkFastAsgn}:
detectCapturedVars(n[0].sons[1], owner, c)
else: assert n[0].kind == nkEmpty
else:
for i in 0..<n.len:
detectCapturedVars(n[i], owner, c)
@@ -687,6 +691,13 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass;
if n.len == 2:
n.sons[1] = liftCapturedVars(n[1], owner, d, c)
if n[1].kind == nkClosure: result = n[1]
of nkReturnStmt:
if n[0].kind in {nkAsgn, nkFastAsgn}:
# we have a `result = result` expression produced by the closure
# transform, let's not touch the LHS in order to make the lifting pass
# correct when `result` is lifted
n[0].sons[1] = liftCapturedVars(n[0].sons[1], owner, d, c)
else: assert n[0].kind == nkEmpty
else:
if owner.isIterator:
if nfLL in n.flags:
@@ -757,6 +768,7 @@ proc liftLambdas*(g: ModuleGraph; fn: PSym, body: PNode; tooEarly: var bool): PN
if d.somethingToDo:
var c = initLiftingPass(fn)
result = liftCapturedVars(body, fn, d, c)
# echo renderTree(result, {renderIds})
if c.envvars.getOrDefault(fn.id) != nil:
result = newTree(nkStmtList, rawClosureCreation(fn, d, c), result)
else:

View File

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

View File

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

View File

@@ -116,7 +116,8 @@ type
callOperator,
parallel,
destructor,
notnil
notnil,
dynamicBindSym
SymbolFilesOption* = enum
disabledSf, writeOnlySf, readOnlySf, v2Sf
@@ -406,8 +407,8 @@ proc mainCommandArg*(conf: ConfigRef): string =
proc existsConfigVar*(conf: ConfigRef; key: string): bool =
result = hasKey(conf.configVars, key)
proc getConfigVar*(conf: ConfigRef; key: string): string =
result = conf.configVars.getOrDefault key
proc getConfigVar*(conf: ConfigRef; key: string, default = ""): string =
result = conf.configVars.getOrDefault(key, default)
proc setConfigVar*(conf: ConfigRef; key, val: string) =
conf.configVars[key] = val
@@ -556,13 +557,28 @@ proc findFile*(conf: ConfigRef; f: string; suppressStdlib = false): string {.pro
result = rawFindFile2(conf, f.toLowerAscii)
patchModule(conf)
const stdlibDirs = [
"pure", "core", "arch",
"pure/collections",
"pure/concurrency", "impure",
"wrappers", "wrappers/linenoise",
"windows", "posix", "js"]
proc findModule*(conf: ConfigRef; modulename, currentModule: string): string =
# returns path to module
const pkgPrefix = "pkg/"
let m = addFileExt(modulename, NimExt)
const stdPrefix = "std/"
var m = addFileExt(modulename, NimExt)
if m.startsWith(pkgPrefix):
result = findFile(conf, m.substr(pkgPrefix.len), suppressStdlib = true)
else:
if m.startsWith(stdPrefix):
let stripped = m.substr(stdPrefix.len)
for candidate in stdlibDirs:
let path = (conf.libpath / candidate / stripped)
if fileExists(path):
m = path
break
let currentPath = currentModule.splitFile.dir
result = currentPath / m
if not existsFile(result):

View File

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

View File

@@ -627,15 +627,23 @@ proc gstmts(g: var TSrcGen, n: PNode, c: TContext, doIndent=true) =
gcoms(g)
if doIndent: dedent(g)
else:
if rfLongMode in c.flags: indentNL(g)
indentNL(g)
gsub(g, n)
gcoms(g)
dedent(g)
optNL(g)
if rfLongMode in c.flags: dedent(g)
proc gcond(g: var TSrcGen, n: PNode) =
if n.kind == nkStmtListExpr:
put(g, tkParLe, "(")
gsub(g, n)
if n.kind == nkStmtListExpr:
put(g, tkParRi, ")")
proc gif(g: var TSrcGen, n: PNode) =
var c: TContext
gsub(g, n.sons[0].sons[0])
gcond(g, n.sons[0].sons[0])
initContext(c)
putWithSpace(g, tkColon, ":")
if longMode(g, n) or (lsub(g, n.sons[0].sons[1]) + g.lineLen > MaxLineLen):
@@ -650,7 +658,7 @@ proc gif(g: var TSrcGen, n: PNode) =
proc gwhile(g: var TSrcGen, n: PNode) =
var c: TContext
putWithSpace(g, tkWhile, "while")
gsub(g, n.sons[0])
gcond(g, n.sons[0])
putWithSpace(g, tkColon, ":")
initContext(c)
if longMode(g, n) or (lsub(g, n.sons[1]) + g.lineLen > MaxLineLen):
@@ -714,7 +722,7 @@ proc gcase(g: var TSrcGen, n: PNode) =
var last = if n.sons[length-1].kind == nkElse: -2 else: -1
if longMode(g, n, 0, last): incl(c.flags, rfLongMode)
putWithSpace(g, tkCase, "case")
gsub(g, n.sons[0])
gcond(g, n.sons[0])
gcoms(g)
optNL(g)
gsons(g, n, c, 1, last)
@@ -785,10 +793,7 @@ proc gblock(g: var TSrcGen, n: PNode) =
if longMode(g, n) or (lsub(g, n.sons[1]) + g.lineLen > MaxLineLen):
incl(c.flags, rfLongMode)
gcoms(g)
# XXX I don't get why this is needed here! gstmts should already handle this!
indentNL(g)
gstmts(g, n.sons[1], c)
dedent(g)
proc gstaticStmt(g: var TSrcGen, n: PNode) =
var c: TContext
@@ -1104,13 +1109,13 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
put(g, tkAccent, "`")
of nkIfExpr:
putWithSpace(g, tkIf, "if")
if n.len > 0: gsub(g, n.sons[0], 0)
if n.len > 0: gcond(g, n.sons[0].sons[0])
putWithSpace(g, tkColon, ":")
if n.len > 0: gsub(g, n.sons[0], 1)
gsons(g, n, emptyContext, 1)
of nkElifExpr:
putWithSpace(g, tkElif, " elif")
gsub(g, n, 0)
gcond(g, n[0])
putWithSpace(g, tkColon, ":")
gsub(g, n, 1)
of nkElseExpr:

View File

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

View File

@@ -505,7 +505,15 @@ proc explicitGenericSym(c: PContext, n: PNode, s: PSym): PNode =
for i in 1..sonsLen(n)-1:
let formal = s.ast.sons[genericParamsPos].sons[i-1].typ
let arg = n[i].typ
var arg = n[i].typ
# try transforming the argument into a static one before feeding it into
# typeRel
if formal.kind == tyStatic and arg.kind != tyStatic:
let evaluated = c.semTryConstExpr(c, n[i])
if evaluated != nil:
arg = newTypeS(tyStatic, c)
arg.sons = @[evaluated.typ]
arg.n = evaluated
let tm = typeRel(m, formal, arg)
if tm in {isNone, isConvertible}: return nil
var newInst = generateInstance(c, s, m.bindings, n.info)

View File

@@ -2031,9 +2031,10 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
c.runnableExamples = newTree(nkStmtList,
newTree(nkImportStmt, newStrNode(nkStrLit, expandFilename(inp))))
let imports = newTree(nkStmtList)
extractImports(n.lastSon, imports)
var saved_lastSon = copyTree n.lastSon
extractImports(saved_lastSon, imports)
for imp in imports: c.runnableExamples.add imp
c.runnableExamples.add newTree(nkBlockStmt, c.graph.emptyNode, copyTree n.lastSon)
c.runnableExamples.add newTree(nkBlockStmt, c.graph.emptyNode, copyTree saved_lastSon)
result = setMs(n, s)
else:
result = c.graph.emptyNode
@@ -2126,7 +2127,7 @@ proc semSetConstr(c: PContext, n: PNode): PNode =
n.sons[i] = semExprWithType(c, n.sons[i])
if typ == nil:
typ = skipTypes(n.sons[i].typ, {tyGenericInst, tyVar, tyLent, tyOrdinal, tyAlias, tySink})
if not isOrdinalType(typ):
if not isOrdinalType(typ, allowEnumWithHoles=true):
localError(c.config, n.info, errOrdinalTypeExpected)
typ = makeRangeType(c, 0, MaxSetElements-1, n.info)
elif lengthOrd(c.config, typ) > MaxSetElements:

View File

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

View File

@@ -207,6 +207,67 @@ proc semBindSym(c: PContext, n: PNode): PNode =
else:
errorUndeclaredIdentifier(c, n.sons[1].info, sl.strVal)
proc opBindSym(c: PContext, scope: PScope, n: PNode, isMixin: int, info: PNode): PNode =
if n.kind notin {nkStrLit, nkRStrLit, nkTripleStrLit, nkIdent}:
localError(c.config, info.info, errStringOrIdentNodeExpected)
return errorNode(c, n)
if isMixin < 0 or isMixin > high(TSymChoiceRule).int:
localError(c.config, info.info, errConstExprExpected)
return errorNode(c, n)
let id = if n.kind == nkIdent: n
else: newIdentNode(getIdent(c.cache, n.strVal), info.info)
let tmpScope = c.currentScope
c.currentScope = scope
let s = qualifiedLookUp(c, id, {checkUndeclared})
if s != nil:
# we need to mark all symbols:
result = symChoice(c, id, s, TSymChoiceRule(isMixin))
else:
errorUndeclaredIdentifier(c, info.info, if n.kind == nkIdent: n.ident.s
else: n.strVal)
c.currentScope = tmpScope
proc semDynamicBindSym(c: PContext, n: PNode): PNode =
# inside regular code, bindSym resolves to the sym-choice
# nodes (see tinspectsymbol)
if not (c.inStaticContext > 0 or getCurrOwner(c).isCompileTimeProc):
return semBindSym(c, n)
if c.graph.vm.isNil:
setupGlobalCtx(c.module, c.graph)
let
vm = PCtx c.graph.vm
# cache the current scope to
# prevent it lost into oblivion
scope = c.currentScope
# cannot use this
# vm.config.features.incl dynamicBindSym
proc bindSymWrapper(a: VmArgs) =
# capture PContext and currentScope
# param description:
# 0. ident, a string literal / computed string / or ident node
# 1. bindSym rule
# 2. info node
a.setResult opBindSym(c, scope, a.getNode(0), a.getInt(1).int, a.getNode(2))
let
# altough we use VM callback here, it is not
# executed like 'normal' VM callback
idx = vm.registerCallback("bindSymImpl", bindSymWrapper)
# dummy node to carry idx information to VM
idxNode = newIntTypeNode(nkIntLit, idx, c.graph.getSysType(TLineInfo(), tyInt))
result = copyNode(n)
for x in n: result.add x
result.add n # info node
result.add idxNode
proc semShallowCopy(c: PContext, n: PNode, flags: TExprFlags): PNode
proc semOf(c: PContext, n: PNode): PNode =
@@ -270,7 +331,11 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
of mOf: result = semOf(c, n)
of mHigh, mLow: result = semLowHigh(c, n, n[0].sym.magic)
of mShallowCopy: result = semShallowCopy(c, n, flags)
of mNBindSym: result = semBindSym(c, n)
of mNBindSym:
if dynamicBindSym notin c.features:
result = semBindSym(c, n)
else:
result = semDynamicBindSym(c, n)
of mProcCall:
result = n
result.typ = n[1].typ

View File

@@ -11,6 +11,7 @@
# included from sem.nim
const
errStringOrIdentNodeExpected = "string or ident node expected"
errStringLiteralExpected = "string literal expected"
errIntLiteralExpected = "integer literal expected"
errWrongNumberOfVariables = "wrong number of variables"
@@ -136,7 +137,7 @@ proc semSet(c: PContext, n: PNode, prev: PType): PType =
addSonSkipIntLit(result, base)
if base.kind in {tyGenericInst, tyAlias, tySink}: base = lastSon(base)
if base.kind != tyGenericParam:
if not isOrdinalType(base):
if not isOrdinalType(base, allowEnumWithHoles = true):
localError(c.config, n.info, errOrdinalTypeExpected)
elif lengthOrd(c.config, base) > MaxSetElements:
localError(c.config, n.info, errSetTooBig)
@@ -1508,26 +1509,20 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
of mSet: result = semSet(c, n, prev)
of mOrdinal: result = semOrdinal(c, n, prev)
of mSeq:
let s = c.graph.sysTypes[tySequence]
assert s != nil
assert prev == nil
result = copyType(s, s.owner, keepId=false)
# XXX figure out why this has children already...
result.sons.setLen 0
result.n = nil
if c.config.selectedGc == gcDestructors:
result.flags = {tfHasAsgn}
let s = c.graph.sysTypes[tySequence]
assert s != nil
assert prev == nil
result = copyType(s, s.owner, keepId=false)
# XXX figure out why this has children already...
result.sons.setLen 0
result.n = nil
if c.config.selectedGc == gcDestructors:
result.flags = {tfHasAsgn}
else:
result.flags = {}
semContainerArg(c, n, "seq", result)
else:
result.flags = {}
semContainerArg(c, n, "seq", result)
when false:
debugT = true
echo "Start!"
#debug result
assert(not containsGenericType(result))
debugT = false
echo "End!"
when false:
result = semContainer(c, n, tySequence, "seq", prev)
if c.config.selectedGc == gcDestructors:
incl result.flags, tfHasAsgn
@@ -1603,8 +1598,14 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
result = prev
of nkSym:
let s = getGenSym(c, n.sym)
if s.kind == skType and s.typ != nil:
var t = s.typ
if s.kind == skType and s.typ != nil or
s.kind == skParam and s.typ.kind == tyTypeDesc:
var t =
if s.kind == skType:
s.typ
else:
internalAssert c.config, s.typ.base.kind != tyNone and prev == nil
s.typ.base
let alias = maybeAliasType(c, t, prev)
if alias != nil:
result = alias

View File

@@ -498,9 +498,15 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
add(result, typeToString(t.sons[i]))
result.add "]"
of tyAnd:
result = typeToString(t.sons[0]) & " and " & typeToString(t.sons[1])
for i, son in t.sons:
result.add(typeToString(son))
if i < t.sons.high:
result.add(" and ")
of tyOr:
result = typeToString(t.sons[0]) & " or " & typeToString(t.sons[1])
for i, son in t.sons:
result.add(typeToString(son))
if i < t.sons.high:
result.add(" or ")
of tyNot:
result = "not " & typeToString(t.sons[0])
of tyExpr:

View File

@@ -616,19 +616,12 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
let rc = instr.regC
case regs[ra].kind
of rkNodeAddr:
# XXX: Workaround for vmgen bug:
let n = regs[rc].regToNode
if (nfIsRef in regs[ra].nodeAddr[].flags or
regs[ra].nodeAddr[].kind == nkNilLit) and nfIsRef notin n.flags:
if regs[ra].nodeAddr[].kind == nkNilLit:
stackTrace(c, tos, pc, errNilAccess)
regs[ra].nodeAddr[][] = n[]
regs[ra].nodeAddr[].flags.incl nfIsRef
# `var object` parameters are sent as rkNodeAddr. When they are mutated
# vmgen generates opcWrDeref, which means that we must dereference
# twice.
# TODO: This should likely be handled differently in vmgen.
elif (nfIsRef notin regs[ra].nodeAddr[].flags and
if (nfIsRef notin regs[ra].nodeAddr[].flags and
nfIsRef notin n.flags):
regs[ra].nodeAddr[][] = n[]
else:
@@ -1229,9 +1222,25 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
node.typ.callConv == ccClosure and node.sons[0].kind == nkNilLit and
node.sons[1].kind == nkNilLit))
of opcNBindSym:
# cannot use this simple check
# if dynamicBindSym notin c.config.features:
# bindSym with static input
decodeBx(rkNode)
regs[ra].node = copyTree(c.constants.sons[rbx])
regs[ra].node.flags.incl nfIsRef
of opcNDynBindSym:
# experimental bindSym
let
rb = instr.regB
rc = instr.regC
idx = int(regs[rb+rc-1].intVal)
callback = c.callbacks[idx].value
args = VmArgs(ra: ra, rb: rb, rc: rc, slots: cast[pointer](regs),
currentException: c.currentExceptionB,
currentLineInfo: c.debug[pc])
callback(args)
regs[ra].node.flags.incl nfIsRef
of opcNChild:
decodeBC(rkNode)
let idx = regs[rc].intVal.int
@@ -1416,7 +1425,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
of opcNGetFile:
decodeB(rkNode)
let n = regs[rb].node
regs[ra].node = newStrNode(nkStrLit, toFilename(c.config, n.info))
regs[ra].node = newStrNode(nkStrLit, toFullPath(c.config, n.info))
regs[ra].node.info = n.info
regs[ra].node.typ = n.typ
of opcNGetLine:
@@ -1780,7 +1789,7 @@ proc getGlobalValue*(c: PCtx; s: PSym): PNode =
include vmops
proc setupGlobalCtx(module: PSym; graph: ModuleGraph) =
proc setupGlobalCtx*(module: PSym; graph: ModuleGraph) =
if graph.vm.isNil:
graph.vm = newCtx(module, graph.cache, graph)
registerAdditionalOps(PCtx graph.vm)

View File

@@ -136,7 +136,7 @@ type
opcLdGlobalAddr, # dest = addr(globals[Bx])
opcLdImmInt, # dest = immediate value
opcNBindSym,
opcNBindSym, opcNDynBindSym,
opcSetType, # dest.typ = types[Bx]
opcTypeTrait,
opcMarshalLoad, opcMarshalStore,
@@ -229,7 +229,8 @@ proc refresh*(c: PCtx, module: PSym) =
c.prc = PProc(blocks: @[])
c.loopIterations = MaxLoopIterations
proc registerCallback*(c: PCtx; name: string; callback: VmCallback) =
proc registerCallback*(c: PCtx; name: string; callback: VmCallback): int {.discardable.} =
result = c.callbacks.len
c.callbacks.add((name, callback))
const

View File

@@ -37,7 +37,9 @@ when hasFFI:
import evalffi
type
TGenFlag = enum gfAddrOf, gfFieldAccess
TGenFlag = enum
gfNode # Affects how variables are loaded - always loads as rkNode
gfNodeAddr # Affects how variables are loaded - always loads as rkNodeAddr
TGenFlags = set[TGenFlag]
proc debugInfo(c: PCtx; info: TLineInfo): string =
@@ -563,7 +565,7 @@ proc genIndex(c: PCtx; n: PNode; arr: PType): TRegister =
proc genAsgnPatch(c: PCtx; le: PNode, value: TRegister) =
case le.kind
of nkBracketExpr:
let dest = c.genx(le.sons[0], {gfAddrOf, gfFieldAccess})
let dest = c.genx(le.sons[0], {gfNode})
let idx = c.genIndex(le.sons[1], le.sons[0].typ)
c.gABC(le, opcWrArr, dest, idx, value)
c.freeTemp(dest)
@@ -571,17 +573,17 @@ proc genAsgnPatch(c: PCtx; le: PNode, value: TRegister) =
of nkDotExpr, nkCheckedFieldExpr:
# XXX field checks here
let left = if le.kind == nkDotExpr: le else: le.sons[0]
let dest = c.genx(left.sons[0], {gfAddrOf, gfFieldAccess})
let dest = c.genx(left.sons[0], {gfNode})
let idx = genField(c, left.sons[1])
c.gABC(left, opcWrObj, dest, idx, value)
c.freeTemp(dest)
of nkDerefExpr, nkHiddenDeref:
let dest = c.genx(le.sons[0], {gfAddrOf})
let dest = c.genx(le.sons[0], {gfNode})
c.gABC(le, opcWrDeref, dest, 0, value)
c.freeTemp(dest)
of nkSym:
if le.sym.isGlobal:
let dest = c.genx(le, {gfAddrOf})
let dest = c.genx(le, {gfNodeAddr})
c.gABC(le, opcWrDeref, dest, 0, value)
c.freeTemp(dest)
else:
@@ -815,6 +817,43 @@ proc genVoidABC(c: PCtx, n: PNode, dest: TDest, opcode: TOpcode) =
c.freeTemp(tmp2)
c.freeTemp(tmp3)
proc genBindSym(c: PCtx; n: PNode; dest: var TDest) =
# nah, cannot use c.config.features because sempass context
# can have local experimental switch
# if dynamicBindSym notin c.config.features:
if n.len == 2: # hmm, reliable?
# bindSym with static input
if n[1].kind in {nkClosedSymChoice, nkOpenSymChoice, nkSym}:
let idx = c.genLiteral(n[1])
if dest < 0: dest = c.getTemp(n.typ)
c.gABx(n, opcNBindSym, dest, idx)
else:
localError(c.config, n.info, "invalid bindSym usage")
else:
# experimental bindSym
if dest < 0: dest = c.getTemp(n.typ)
let x = c.getTempRange(n.len, slotTempUnknown)
# callee symbol
var tmp0 = TDest(x)
c.genLit(n.sons[0], tmp0)
# original parameters
for i in 1..<n.len-2:
var r = TRegister(x+i)
c.gen(n.sons[i], r)
# info node
var tmp1 = TDest(x+n.len-2)
c.genLit(n.sons[^2], tmp1)
# payload idx
var tmp2 = TDest(x+n.len-1)
c.genLit(n.sons[^1], tmp2)
c.gABC(n, opcNDynBindSym, dest, x, n.len)
c.freeTempRange(x, n.len)
proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
case m
of mAnd: c.genAndOr(n, opcFJmp, dest)
@@ -1135,13 +1174,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
of mNNewNimNode: genBinaryABC(c, n, dest, opcNNewNimNode)
of mNCopyNimNode: genUnaryABC(c, n, dest, opcNCopyNimNode)
of mNCopyNimTree: genUnaryABC(c, n, dest, opcNCopyNimTree)
of mNBindSym:
if n[1].kind in {nkClosedSymChoice, nkOpenSymChoice, nkSym}:
let idx = c.genLiteral(n[1])
if dest < 0: dest = c.getTemp(n.typ)
c.gABx(n, opcNBindSym, dest, idx)
else:
localError(c.config, n.info, "invalid bindSym usage")
of mNBindSym: genBindSym(c, n, dest)
of mStrToIdent: genUnaryABC(c, n, dest, opcStrToIdent)
of mEqIdent: genBinaryABC(c, n, dest, opcEqIdent)
of mEqNimrodNode: genBinaryABC(c, n, dest, opcEqNimrodNode)
@@ -1252,41 +1285,21 @@ proc canElimAddr(n: PNode): PNode =
# addr ( deref ( x )) --> x
result = n.sons[0].sons[0]
proc genAddrDeref(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode;
flags: TGenFlags) =
# a nop for certain types
let isAddr = opc in {opcAddrNode, opcAddrReg}
if isAddr and (let m = canElimAddr(n); m != nil):
proc genAddr(c: PCtx, n: PNode, dest: var TDest, flags: TGenFlags) =
if (let m = canElimAddr(n); m != nil):
gen(c, m, dest, flags)
return
let af = if n[0].kind in {nkBracketExpr, nkDotExpr, nkCheckedFieldExpr}: {gfAddrOf, gfFieldAccess}
else: {gfAddrOf}
let newflags = if isAddr: flags+af else: flags
# consider:
# proc foo(f: var ref int) =
# f = new(int)
# proc blah() =
# var x: ref int
# foo x
#
# The type of 'f' is 'var ref int' and of 'x' is 'ref int'. Hence for
# nkAddr we must not use 'unneededIndirection', but for deref we use it.
if not isAddr and unneededIndirection(n.sons[0]):
gen(c, n.sons[0], dest, newflags)
if gfAddrOf notin flags and fitsRegister(n.typ):
c.gABC(n, opcNodeToReg, dest, dest)
elif isAddr and isGlobal(n.sons[0]):
let af = if n[0].kind in {nkBracketExpr, nkDotExpr, nkCheckedFieldExpr}: {gfNode}
else: {gfNodeAddr}
let newflags = flags-{gfNode, gfNodeAddr}+af
if isGlobal(n.sons[0]):
gen(c, n.sons[0], dest, flags+af)
else:
let tmp = c.genx(n.sons[0], newflags)
if dest < 0: dest = c.getTemp(n.typ)
if not isAddr:
gABC(c, n, opc, dest, tmp)
assert n.typ != nil
if gfAddrOf notin flags and fitsRegister(n.typ):
c.gABC(n, opcNodeToReg, dest, dest)
elif c.prc.slots[tmp].kind >= slotTempUnknown:
if c.prc.slots[tmp].kind >= slotTempUnknown:
gABC(c, n, opcAddrNode, dest, tmp)
# hack ahead; in order to fix bug #1781 we mark the temporary as
# permanent, so that it's not used for anything else:
@@ -1297,6 +1310,19 @@ proc genAddrDeref(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode;
gABC(c, n, opcAddrReg, dest, tmp)
c.freeTemp(tmp)
proc genDeref(c: PCtx, n: PNode, dest: var TDest, flags: TGenFlags) =
if unneededIndirection(n.sons[0]):
gen(c, n.sons[0], dest, flags)
if {gfNodeAddr, gfNode} * flags == {} and fitsRegister(n.typ):
c.gABC(n, opcNodeToReg, dest, dest)
else:
let tmp = c.genx(n.sons[0], flags)
if dest < 0: dest = c.getTemp(n.typ)
gABC(c, n, opcLdDeref, dest, tmp)
assert n.typ != nil
if {gfNodeAddr, gfNode} * flags == {} and fitsRegister(n.typ):
c.gABC(n, opcNodeToReg, dest, dest)
proc whichAsgnOpc(n: PNode): TOpcode =
case n.typ.skipTypes(abstractRange-{tyTypeDesc}).kind
of tyBool, tyChar, tyEnum, tyOrdinal, tyInt..tyInt64, tyUInt..tyUInt64:
@@ -1382,7 +1408,7 @@ proc preventFalseAlias(c: PCtx; n: PNode; opc: TOpcode;
proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) =
case le.kind
of nkBracketExpr:
let dest = c.genx(le.sons[0], {gfAddrOf, gfFieldAccess})
let dest = c.genx(le.sons[0], {gfNode})
let idx = c.genIndex(le.sons[1], le.sons[0].typ)
let tmp = c.genx(ri)
if le.sons[0].typ.skipTypes(abstractVarRange-{tyTypeDesc}).kind in {
@@ -1394,13 +1420,13 @@ proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) =
of nkDotExpr, nkCheckedFieldExpr:
# XXX field checks here
let left = if le.kind == nkDotExpr: le else: le.sons[0]
let dest = c.genx(left.sons[0], {gfAddrOf, gfFieldAccess})
let dest = c.genx(left.sons[0], {gfNode})
let idx = genField(c, left.sons[1])
let tmp = c.genx(ri)
c.preventFalseAlias(left, opcWrObj, dest, idx, tmp)
c.freeTemp(tmp)
of nkDerefExpr, nkHiddenDeref:
let dest = c.genx(le.sons[0], {gfAddrOf})
let dest = c.genx(le.sons[0], {gfNode})
let tmp = c.genx(ri)
c.preventFalseAlias(le, opcWrDeref, dest, 0, tmp)
c.freeTemp(tmp)
@@ -1409,7 +1435,7 @@ proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) =
checkCanEval(c, le)
if s.isGlobal:
withTemp(tmp, le.typ):
c.gen(le, tmp, {gfAddrOf})
c.gen(le, tmp, {gfNodeAddr})
let val = c.genx(ri)
c.preventFalseAlias(le, opcWrDeref, tmp, 0, val)
c.freeTemp(val)
@@ -1427,7 +1453,7 @@ proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) =
else:
gen(c, ri, dest)
else:
let dest = c.genx(le, {gfAddrOf})
let dest = c.genx(le, {gfNodeAddr})
genAsgn(c, dest, ri, requiresCopy)
proc genTypeLit(c: PCtx; t: PType; dest: var TDest) =
@@ -1463,6 +1489,8 @@ proc genGlobalInit(c: PCtx; n: PNode; s: PSym) =
c.freeTemp(tmp)
proc genRdVar(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
# gfNodeAddr and gfNode are mutually exclusive
assert card(flags * {gfNodeAddr, gfNode}) < 2
let s = n.sym
if s.isGlobal:
if sfCompileTime in s.flags or c.mode == emRepl:
@@ -1474,13 +1502,13 @@ proc genRdVar(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
else: genGlobalInit(c, n, s)
if dest < 0: dest = c.getTemp(n.typ)
assert s.typ != nil
if gfAddrOf notin flags and fitsRegister(s.typ):
if gfNodeAddr in flags:
c.gABx(n, opcLdGlobalAddr, dest, s.position)
elif fitsRegister(s.typ) and gfNode notin flags:
var cc = c.getTemp(n.typ)
c.gABx(n, opcLdGlobal, cc, s.position)
c.gABC(n, opcNodeToReg, dest, cc)
c.freeTemp(cc)
elif {gfAddrOf, gfFieldAccess} * flags == {gfAddrOf}:
c.gABx(n, opcLdGlobalAddr, dest, s.position)
else:
c.gABx(n, opcLdGlobal, dest, s.position)
else:
@@ -1498,7 +1526,8 @@ proc genRdVar(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
cannotEval(c, n)
template needsRegLoad(): untyped =
gfAddrOf notin flags and fitsRegister(n.typ.skipTypes({tyVar, tyLent}))
{gfNode, gfNodeAddr} * flags == {} and
fitsRegister(n.typ.skipTypes({tyVar, tyLent}))
proc genArrAccess2(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode;
flags: TGenFlags) =
@@ -1634,7 +1663,7 @@ proc genVarSection(c: PCtx; n: PNode) =
c.globals.add(sa)
s.position = c.globals.len
if a.sons[2].kind != nkEmpty:
let tmp = c.genx(a.sons[0], {gfAddrOf})
let tmp = c.genx(a.sons[0], {gfNodeAddr})
let val = c.genx(a.sons[2])
c.genAdditionalCopy(a.sons[2], opcWrDeref, tmp, 0, val)
c.freeTemp(val)
@@ -1839,8 +1868,8 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
of nkDotExpr: genObjAccess(c, n, dest, flags)
of nkCheckedFieldExpr: genCheckedObjAccess(c, n, dest, flags)
of nkBracketExpr: genArrAccess(c, n, dest, flags)
of nkDerefExpr, nkHiddenDeref: genAddrDeref(c, n, dest, opcLdDeref, flags)
of nkAddr, nkHiddenAddr: genAddrDeref(c, n, dest, opcAddrNode, flags)
of nkDerefExpr, nkHiddenDeref: genDeref(c, n, dest, flags)
of nkAddr, nkHiddenAddr: genAddr(c, n, dest, flags)
of nkIfStmt, nkIfExpr: genIf(c, n, dest)
of nkWhenStmt:
# This is "when nimvm" node. Chose the first branch.

View File

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

View File

@@ -6229,10 +6229,10 @@ imported:
:test: "nim c $1"
:status: 1
import strutils except `%`, toUpper
import strutils except `%`, toUpperAscii
# doesn't work then:
echo "$1" % "abc".toUpper
echo "$1" % "abc".toUpperAscii
It is not checked that the ``except`` list is really exported from the module.
@@ -6261,24 +6261,24 @@ A module alias can be introduced via the ``as`` keyword:
echo su.format("$1", "lalelu")
The original module name is then not accessible. The
notations ``path/to/module`` or ``path.to.module`` or ``"path/to/module"``
can be used to refer to a module in subdirectories:
The original module name is then not accessible. The notations
``path/to/module`` or ``"path/to/module"`` can be used to refer to a module
in subdirectories:
.. code-block:: nim
import lib.pure.strutils, lib/pure/os, "lib/pure/times"
import lib/pure/os, "lib/pure/times"
Note that the module name is still ``strutils`` and not ``lib.pure.strutils``
Note that the module name is still ``strutils`` and not ``lib/pure/strutils``
and so one **cannot** do:
.. code-block:: nim
import lib.pure.strutils
echo lib.pure.strutils
import lib/pure/strutils
echo lib/pure/strutils.toUpperAscii("abc")
Likewise the following does not make sense as the name is ``strutils`` already:
.. code-block:: nim
import lib.pure.strutils as strutils
import lib/pure/strutils as strutils
Collective imports from a directory
@@ -6297,7 +6297,8 @@ name is not a valid Nim identifier it needs to be a string literal:
Pseudo import/include paths
~~~~~~~~~~~~~~~~~~~~~~~~~~~
A directory can also be a so called "pseudo directory".
A directory can also be a so called "pseudo directory". They can be used to
avoid ambiguity when there are multiple modules with the same path.
There are two pseudo directories:

View File

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

View File

@@ -50,6 +50,7 @@ Boot options:
Commands for core developers:
web [options] generates the website and the full documentation
(see `nimweb.nim` for cmd line options)
website [options] generates only the website
csource -d:release builds the C sources for installation
pdf builds the PDF documentation
@@ -241,7 +242,15 @@ proc zip(args: string) =
exec("$# --var:version=$# --var:mingw=none --main:compiler/nim.nim zip compiler/installer.ini" %
["tools/niminst/niminst".exe, VersionAsString])
proc ensureCleanGit() =
let (outp, status) = osproc.execCmdEx("git diff")
if outp.len != 0:
quit "Not a clean git repository; 'git diff' not empty!"
if status != 0:
quit "Not a clean git repository; 'git diff' returned non-zero!"
proc xz(args: string) =
ensureCleanGit()
bundleNimbleSrc()
bundleNimsuggest(false)
nimexec("cc -r $2 --var:version=$1 --var:mingw=none --main:compiler/nim.nim scripts compiler/installer.ini" %

View File

@@ -382,16 +382,24 @@ type
{.deprecated: [TBindSymRule: BindSymRule].}
proc bindSym*(ident: static[string], rule: BindSymRule = brClosed): NimNode {.
proc bindSym*(ident: string | NimNode, rule: BindSymRule = brClosed): NimNode {.
magic: "NBindSym", noSideEffect.}
## creates a node that binds `ident` to a symbol node. The bound symbol
## may be an overloaded symbol.
## if `ident` is a NimNode, it must have nkIdent kind.
## If ``rule == brClosed`` either an ``nkClosedSymChoice`` tree is
## returned or ``nkSym`` if the symbol is not ambiguous.
## If ``rule == brOpen`` either an ``nkOpenSymChoice`` tree is
## returned or ``nkSym`` if the symbol is not ambiguous.
## If ``rule == brForceOpen`` always an ``nkOpenSymChoice`` tree is
## returned even if the symbol is not ambiguous.
##
## experimental feature:
## use {.experimental: "dynamicBindSym".} to activate it
## if called from template / regular code, `ident` and `rule` must be
## constant expression / literal value.
## if called from macros / compile time procs / static blocks,
## `ident` and `rule` can be VM computed value.
proc genSym*(kind: NimSymKind = nskLet; ident = ""): NimNode {.
magic: "NGenSym", noSideEffect.}
@@ -425,6 +433,7 @@ proc getColumn(arg: NimNode): int {.magic: "NLineInfo", noSideEffect.}
proc getFile(arg: NimNode): string {.magic: "NLineInfo", noSideEffect.}
proc lineInfoObj*(n: NimNode): LineInfo {.compileTime.} =
## returns ``LineInfo`` of ``n``, using absolute path for ``filename``
result.filename = n.getFile
result.line = n.getLine
result.column = n.getColumn
@@ -1284,7 +1293,7 @@ proc customPragmaNode(n: NimNode): NimNode =
let
typ = n.getTypeInst()
if typ.kind == nnkBracketExpr and typ.len > 1 and typ[1].kind == nnkProcTy:
if typ.kind == nnkBracketExpr and typ.len > 1 and typ[1].kind == nnkProcTy:
return typ[1][1]
elif typ.typeKind == ntyTypeDesc:
let impl = typ[1].getImpl()
@@ -1319,6 +1328,8 @@ proc customPragmaNode(n: NimNode): NimNode =
if identDefs.kind == nnkRecCase:
identDefsStack.add(identDefs[0])
for i in 1..<identDefs.len:
# if it is and empty branch, skip
if identDefs[i][0].kind == nnkNilLit: continue
if identDefs[i][1].kind == nnkIdentDefs:
identDefsStack.add(identDefs[i][1])
else: # nnkRecList

View File

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

View File

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

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."
for item in other: discard exclImpl(s, item)
proc pop*[A](s: var HashSet[A]): A =
## Remove and return an arbitrary element from the set `s`.
##
## Raises KeyError if the set `s` is empty.
##
for h in 0..high(s.data):
if isFilled(s.data[h].hcode):
result = s.data[h].key
excl(s, result)
return result
raise newException(KeyError, "set is empty")
proc containsOrIncl*[A](s: var HashSet[A], key: A): bool =
## Includes `key` in the set `s` and tells if `key` was added to `s`.
##

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -41,7 +41,7 @@
## immediately.
##
## .. code-block:: Nim
## var socket = newSocket()
## var socket = newSocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)
## socket.sendTo("192.168.0.1", Port(27960), "status\n")
##
## Creating a server

View File

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

View File

@@ -147,7 +147,7 @@ else: # UNIX-like operating system
DirSep* = '/'
AltSep* = DirSep
PathSep* = ':'
FileSystemCaseSensitive* = true
FileSystemCaseSensitive* = when defined(macosx): false else: true
ExeExt* = ""
ScriptExt* = ""
DynlibFormat* = when defined(macosx): "lib$1.dylib" else: "lib$1.so"
@@ -410,6 +410,11 @@ proc cmpPaths*(pathA, pathB: string): int {.
## | 0 iff pathA == pathB
## | < 0 iff pathA < pathB
## | > 0 iff pathA > pathB
runnableExamples:
when defined(macosx):
doAssert cmpPaths("foo", "Foo") == 0
elif defined(posix):
doAssert cmpPaths("foo", "Foo") > 0
if FileSystemCaseSensitive:
result = cmp(pathA, pathB)
else:

View File

@@ -110,7 +110,7 @@ proc random*[T](a: openArray[T]): T {.deprecated.} =
## Use ``rand`` instead.
result = a[random(a.low..a.len)]
proc rand*(r: var Rand; max: int): int {.benign.} =
proc rand*(r: var Rand; max: Natural): int {.benign.} =
## Returns a random number in the range 0..max. The sequence of
## random number is always the same, unless `randomize` is called
## which initializes the random number generator with a "random"
@@ -128,7 +128,7 @@ proc rand*(max: int): int {.benign.} =
## number, i.e. a tickcount.
rand(state, max)
proc rand*(r: var Rand; max: float): float {.benign.} =
proc rand*(r: var Rand; max: range[0.0 .. high(float)]): float {.benign.} =
## Returns a random number in the range 0..max. The sequence of
## random number is always the same, unless `randomize` is called
## which initializes the random number generator with a "random"
@@ -218,4 +218,17 @@ when isMainModule:
doAssert rand(0) == 0
doAssert rand("a") == 'a'
when compileOption("rangeChecks"):
try:
discard rand(-1)
doAssert false
except RangeError:
discard
try:
discard rand(-1.0)
doAssert false
except RangeError:
discard
main()

View File

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

View File

@@ -181,10 +181,27 @@ elif defined(windows):
type
Month* = enum ## Represents a month. Note that the enum starts at ``1``, so ``ord(month)`` will give
## the month number in the range ``[1..12]``.
mJan = 1, mFeb, mMar, mApr, mMay, mJun, mJul, mAug, mSep, mOct, mNov, mDec
mJan = (1, "January")
mFeb = "February"
mMar = "March"
mApr = "April"
mMay = "May"
mJun = "June"
mJul = "July"
mAug = "August"
mSep = "September"
mOct = "October"
mNov = "November"
mDec = "December"
WeekDay* = enum ## Represents a weekday.
dMon, dTue, dWed, dThu, dFri, dSat, dSun
dMon = "Monday"
dTue = "Tuesday"
dWed = "Wednesday"
dThu = "Thursday"
dFri = "Friday"
dSat = "Saturday"
dSun = "Sunday"
MonthdayRange* = range[1..31]
HourRange* = range[0..23]
@@ -1074,20 +1091,6 @@ proc getClockStr*(): string {.rtl, extern: "nt$1", tags: [TimeEffect].} =
result = intToStr(ti.hour, 2) & ':' & intToStr(ti.minute, 2) &
':' & intToStr(ti.second, 2)
proc `$`*(day: WeekDay): string =
## Stringify operator for ``WeekDay``.
const lookup: array[WeekDay, string] = ["Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday", "Sunday"]
return lookup[day]
proc `$`*(m: Month): string =
## Stringify operator for ``Month``.
const lookup: array[Month, string] = ["January", "February", "March",
"April", "May", "June", "July", "August", "September", "October",
"November", "December"]
return lookup[m]
proc toParts* (ti: TimeInterval): TimeIntervalParts =
## Converts a `TimeInterval` into an array consisting of its time units,
## starting with nanoseconds and ending with years

View File

@@ -1479,8 +1479,8 @@ when defined(nimdoc):
##
## Note that this is a *runtime* call and using ``quit`` inside a macro won't
## have any compile time effect. If you need to stop the compiler inside a
## macro, use the `error <manual.html#error-pragma>`_ or `fatal
## <manual.html#fatal-pragma>`_ pragmas.
## macro, use the `error <manual.html#pragmas-error-pragma>`_ or `fatal
## <manual.html#pragmas-fatal-pragma>`_ pragmas.
elif defined(genode):
include genode/env
@@ -2438,7 +2438,7 @@ iterator fieldPairs*[T: tuple|object](x: T): RootObj {.
## When you iterate over objects with different field types you have to use
## the compile time ``when`` instead of a runtime ``if`` to select the code
## you want to run for each type. To perform the comparison use the `is
## operator <manual.html#is-operator>`_. Example:
## operator <manual.html#generics-is-operator>`_. Example:
##
## .. code-block:: Nim
##
@@ -3788,7 +3788,9 @@ template doAssert*(cond: bool, msg = "") =
## same as `assert` but is always turned on and not affected by the
## ``--assertions`` command line switch.
bind instantiationInfo
{.line: instantiationInfo().}:
# NOTE: `true` is correct here; --excessiveStackTrace:on will control whether
# or not to output full paths.
{.line: instantiationInfo(-1, true).}:
if not cond:
raiseAssert(astToStr(cond) & ' ' &
instantiationInfo(-1, false).fileName & '(' &

View File

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

View File

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

View File

@@ -548,7 +548,10 @@ proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer =
gcTrace(res, csAllocated)
track("growObj old", ol, 0)
track("growObj new", res, newsize)
when reallyDealloc:
when defined(nimIncrSeqV3):
# since we steal the old seq's contents, we set the old length to 0.
cast[PGenericSeq](old).len = 0
elif reallyDealloc:
sysAssert(allocInv(gch.region), "growObj before dealloc")
if ol.refcount shr rcShift <=% 1:
# free immediately to save space:

View File

@@ -57,6 +57,9 @@ when defined(nimTypeNames):
for i in 0 .. n-1:
c_fprintf(stdout, "[Heap] %s: #%ld; bytes: %ld\n", a[i][0], a[i][1], a[i][2])
c_fprintf(stdout, "[Heap] total number of bytes: %ld\n", totalAllocated)
when defined(nimTypeNames):
let (allocs, deallocs) = getMemCounters()
c_fprintf(stdout, "[Heap] allocs/deallocs: %ld/%ld\n", allocs, deallocs)
when defined(nimGcRefLeak):
proc oomhandler() =

View File

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

View File

@@ -144,8 +144,13 @@ proc addChar(s: NimString, c: char): NimString =
result = s
if result.len >= result.space:
let r = resize(result.space)
result = cast[NimString](growObj(result,
sizeof(TGenericSeq) + r + 1))
when defined(nimIncrSeqV3):
result = rawNewStringNoInit(r)
result.len = s.len
copyMem(addr result.data[0], unsafeAddr(s.data[0]), s.len+1)
else:
result = cast[NimString](growObj(result,
sizeof(TGenericSeq) + r + 1))
result.reserved = r
result.data[result.len] = c
result.data[result.len+1] = '\0'
@@ -188,8 +193,13 @@ proc resizeString(dest: NimString, addlen: int): NimString {.compilerRtl.} =
elif dest.len + addlen <= dest.space:
result = dest
else: # slow path:
var sp = max(resize(dest.space), dest.len + addlen)
result = cast[NimString](growObj(dest, sizeof(TGenericSeq) + sp + 1))
let sp = max(resize(dest.space), dest.len + addlen)
when defined(nimIncrSeqV3):
result = rawNewStringNoInit(sp)
result.len = dest.len
copyMem(addr result.data[0], unsafeAddr(dest.data[0]), dest.len+1)
else:
result = cast[NimString](growObj(dest, sizeof(TGenericSeq) + sp + 1))
result.reserved = sp
#result = rawNewString(sp)
#copyMem(result, dest, dest.len + sizeof(TGenericSeq))
@@ -212,7 +222,15 @@ proc setLengthStr(s: NimString, newLen: int): NimString {.compilerRtl.} =
elif n <= s.space:
result = s
else:
result = resizeString(s, n)
let sp = max(resize(s.space), newLen)
when defined(nimIncrSeqV3):
result = rawNewStringNoInit(sp)
result.len = s.len
copyMem(addr result.data[0], unsafeAddr(s.data[0]), s.len+1)
zeroMem(addr result.data[s.len], newLen - s.len)
result.reserved = sp
else:
result = resizeString(s, n)
result.len = n
result.data[n] = '\0'
@@ -242,6 +260,9 @@ proc incrSeqV2(seq: PGenericSeq, elemSize: int): PGenericSeq {.compilerProc.} =
GenericSeqSize))
result.reserved = r
template `+!`(p: pointer, s: int): pointer =
cast[pointer](cast[int](p) +% s)
proc incrSeqV3(s: PGenericSeq, typ: PNimType): PGenericSeq {.compilerProc.} =
if s == nil:
result = cast[PGenericSeq](newSeq(typ, 1))
@@ -250,9 +271,16 @@ proc incrSeqV3(s: PGenericSeq, typ: PNimType): PGenericSeq {.compilerProc.} =
result = s
if result.len >= result.space:
let r = resize(result.space)
result = cast[PGenericSeq](growObj(result, typ.base.size * r +
when defined(nimIncrSeqV3):
result = cast[PGenericSeq](newSeq(typ, r))
result.len = s.len
copyMem(result +! GenericSeqSize, s +! GenericSeqSize, s.len * typ.base.size)
# since we steal the content from 's', it's crucial to set s's len to 0.
s.len = 0
else:
result = cast[PGenericSeq](growObj(result, typ.base.size * r +
GenericSeqSize))
result.reserved = r
result.reserved = r
proc setLengthSeq(seq: PGenericSeq, elemSize, newLen: int): PGenericSeq {.
compilerRtl, inl.} =
@@ -296,7 +324,40 @@ proc setLengthSeq(seq: PGenericSeq, elemSize, newLen: int): PGenericSeq {.
proc setLengthSeqV2(s: PGenericSeq, typ: PNimType, newLen: int): PGenericSeq {.
compilerRtl.} =
sysAssert typ.kind == tySequence, "setLengthSeqV2: type is not a seq"
if s == nil:
result = cast[PGenericSeq](newSeq(typ, newLen))
else:
result = setLengthSeq(s, typ.base.size, newLen)
when defined(nimIncrSeqV3):
let elemSize = typ.base.size
if s.space < newLen:
let r = max(resize(s.space), newLen)
result = cast[PGenericSeq](newSeq(typ, r))
copyMem(result +! GenericSeqSize, s +! GenericSeqSize, s.len * elemSize)
# since we steal the content from 's', it's crucial to set s's len to 0.
s.len = 0
elif newLen < s.len:
result = s
# we need to decref here, otherwise the GC leaks!
when not defined(boehmGC) and not defined(nogc) and
not defined(gcMarkAndSweep) and not defined(gogc) and
not defined(gcRegions):
if ntfNoRefs notin typ.base.flags:
for i in newLen..result.len-1:
forAllChildrenAux(cast[pointer](cast[ByteAddress](result) +%
GenericSeqSize +% (i*%elemSize)),
extGetCellType(result).base, waZctDecRef)
# XXX: zeroing out the memory can still result in crashes if a wiped-out
# cell is aliased by another pointer (ie proc parameter or a let variable).
# This is a tough problem, because even if we don't zeroMem here, in the
# presence of user defined destructors, the user will expect the cell to be
# "destroyed" thus creating the same problem. We can destoy the cell in the
# finalizer of the sequence, but this makes destruction non-deterministic.
zeroMem(cast[pointer](cast[ByteAddress](result) +% GenericSeqSize +%
(newLen*%elemSize)), (result.len-%newLen) *% elemSize)
else:
result = s
result.len = newLen
else:
result = setLengthSeq(s, typ.base.size, newLen)

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]()
if val != $(i*i): echo "bug ", val
if getOccupiedMem() > 3000_000: quit("still a leak!")
if getOccupiedMem() > 5000_000: quit("still a leak!")
echo "success"
main()

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

View File

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

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 """
msg: '''initApple
deinitApple
Coral
enum
redCoral, blackCoral'''
output: '''TFoo
TBar'''
"""
@@ -23,3 +28,40 @@ macro test: untyped =
bindSym("TBar"))
test()
# issue 7827, bindSym power up
{.experimental: "dynamicBindSym".}
type
Apple = ref object
name: string
color: int
weight: int
proc initApple(name: string): Apple =
discard
proc deinitApple(x: Apple) =
discard
macro wrapObject(obj: typed, n: varargs[untyped]): untyped =
let m = n[0]
for x in m:
var z = bindSym x
echo z.repr
wrapObject(Apple):
initApple
deinitApple
type
Coral = enum
redCoral
blackCoral
macro mixer(): untyped =
let m = "Co" & "ral"
let x = bindSym(m)
echo x.repr
echo getType(x).repr
mixer()

View File

@@ -43,4 +43,35 @@ macro repr_and_parse(fn: typed): typed =
echo 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
TMyRecord {.final.} = object
x, y: int
@@ -7,4 +9,20 @@ type
r: float
s: string
TMyEnum = enum
tmOne, tmTwo, tmThree, tmFour
TMyArray1 = array[3, uint8]
TMyArray2 = array[1..3, int32]
TMyArray3 = array[TMyEnum, float64]
const
mysize1 = sizeof(TMyArray1)
mysize2 = sizeof(TMyArray2)
mysize3 = sizeof(TMyArray3)
write(stdout, sizeof(TMyRecord))
echo ' ', mysize1, ' ', mysize2, ' ',mysize3

View File

@@ -154,3 +154,23 @@ block:
let a: proc(x: int) {.defaultValue(5).} = nil
static:
doAssert hasCustomPragma(a.type, defaultValue)
# bug #8371
template thingy {.pragma.}
type
Cardinal = enum
north, east, south, west
Something = object
a: float32
case cardinal: Cardinal
of north:
b {.thingy.}: int
of east:
c: int
of south: discard
else: discard
var foo: Something
foo.cardinal = north
doAssert foo.b.hasCustomPragma(thingy) == true

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
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]
n3: Option[string]
n4: Option[bool]
var j0 = parseJson("""{"n1": 1, "n2": null, "n3": null, "n4": null}""")
let j0Deser = j0.to(Obj)
doAssert j0Deser.n1 == 1
@@ -411,10 +411,109 @@ when isMainModule:
doAssert dataDeser.a == 1
doAssert dataDeser.f == 6
doAssert dataDeser.i == 9.9'f32
# deserialize directly into a table
block:
let s = """{"a": 1, "b": 2}"""
let t = parseJson(s).to(Table[string, int])
doAssert t["a"] == 1
doAssert t["b"] == 2
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
removeFile("a")
when defined(Linux) or defined(macosx):
when defined(posix):
block normalizedPath:
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
x = cast[ptr int](alloc(7))
assert x != nil
doAssert x != nil
x = cast[ptr int](x.realloc(2))
assert x != nil
doAssert x != nil
x.dealloc()
x = createU(int, 3)
assert x != nil
doAssert x != nil
x.dealloc()
x = create(int, 4)
assert cast[ptr array[4, int]](x)[0] == 0
assert cast[ptr array[4, int]](x)[1] == 0
assert cast[ptr array[4, int]](x)[2] == 0
assert cast[ptr array[4, int]](x)[3] == 0
doAssert cast[ptr array[4, int]](x)[0] == 0
doAssert cast[ptr array[4, int]](x)[1] == 0
doAssert cast[ptr array[4, int]](x)[2] == 0
doAssert cast[ptr array[4, int]](x)[3] == 0
x = x.resize(4)
assert x != nil
doAssert x != nil
x.dealloc()
x = cast[ptr int](allocShared(100))
assert x != nil
doAssert x != nil
deallocShared(x)
x = createSharedU(int, 3)
assert x != nil
doAssert x != nil
x.deallocShared()
x = createShared(int, 3)
assert x != nil
assert cast[ptr array[3, int]](x)[0] == 0
assert cast[ptr array[3, int]](x)[1] == 0
assert cast[ptr array[3, int]](x)[2] == 0
doAssert x != nil
doAssert cast[ptr array[3, int]](x)[0] == 0
doAssert cast[ptr array[3, int]](x)[1] == 0
doAssert cast[ptr array[3, int]](x)[2] == 0
assert x != nil
doAssert x != nil
x = cast[ptr int](x.resizeShared(2))
assert x != nil
doAssert x != nil
x.deallocShared()
x = create(int, 10)
assert x != nil
doAssert x != nil
x = x.resize(12)
assert x != nil
doAssert x != nil
x.dealloc()
x = createShared(int, 1)
assert x != nil
doAssert x != nil
x = x.resizeShared(1)
assert x != nil
doAssert x != nil
x.deallocShared()
x = cast[ptr int](alloc0(125 shl 23))

View File

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

View File

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

View File

@@ -1,3 +1,6 @@
discard """
"""
import
unittest, osproc, streams, os, strformat
const STRING_DATA = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet."
@@ -36,7 +39,7 @@ proc verifyFileSize(sz: int64) =
discard execProcess(&"dd if=/dev/zero of={fn} bs=1000000 count={size_in_mb}")
doAssert os.getFileSize(fn) == sz # Verify OS filesize by string
var f = open(fn)
doAssert f.getFileSize() == sz # Verify file handle filesize
f.close()

View File

@@ -1,3 +1,6 @@
discard """
"""
import os
import osproc
import parseopt2
@@ -7,12 +10,12 @@ let argv = commandLineParams()
if argv == @[]:
# this won't work with spaces
assert execShellCmd(getAppFilename() & " \"foo bar\" --aa:bar=a --a=c:d --ab -c --a[baz]:doo") == 0
doAssert execShellCmd(getAppFilename() & " \"foo bar\" --aa:bar=a --a=c:d --ab -c --a[baz]:doo") == 0
else:
let f = toSeq(getopt())
echo f.repr
assert f[0].kind == cmdArgument and f[0].key == "foo bar" and f[0].val == ""
assert f[1].kind == cmdLongOption and f[1].key == "aa" and f[1].val == "bar=a"
assert f[2].kind == cmdLongOption and f[2].key == "a=c" and f[2].val == "d"
assert f[3].kind == cmdLongOption and f[3].key == "ab" and f[3].val == ""
assert f[4].kind == cmdShortOption and f[4].key == "c" and f[4].val == ""
doAssert f[0].kind == cmdArgument and f[0].key == "foo bar" and f[0].val == ""
doAssert f[1].kind == cmdLongOption and f[1].key == "aa" and f[1].val == "bar=a"
doAssert f[2].kind == cmdLongOption and f[2].key == "a=c" and f[2].val == "d"
doAssert f[3].kind == cmdLongOption and f[3].key == "ab" and f[3].val == ""
doAssert f[4].kind == cmdShortOption and f[4].key == "c" and f[4].val == ""

View File

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

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