fix merge conflict

This commit is contained in:
Andreas Rumpf
2018-07-18 09:46:30 +02:00
65 changed files with 2187 additions and 917 deletions

18
bin/nim-gdb Executable file
View File

@@ -0,0 +1,18 @@
#!/usr/bin/env bash
# Exit if anything fails
set -e
# Find out where the pretty printer Python module is
NIM_SYSROOT=$(dirname $(dirname $(readlink -e $(which nim))))
GDB_PYTHON_MODULE_PATH="$NIM_SYSROOT/tools/nim-gdb.py"
# Run GDB with the additional arguments that load the pretty printers
# Set the environment variable `NIM_GDB` to overwrite the call to a
# different/specific command (defaults to `gdb`).
NIM_GDB="${NIM_GDB:-gdb}"
# exec replaces the new process of bash with gdb. It is always good to
# have fewer processes.
exec ${NIM_GDB} \
-eval-command "source $GDB_PYTHON_MODULE_PATH" \
"$@"

View File

@@ -50,9 +50,15 @@
- For string inputs, ``strutils.isUpperAscii`` and ``strutils.isLowerAscii`` now
require a second mandatory parameter ``skipNonAlpha``.
- ``osLastError`` is now marked with ``sideEffect``
- The procs ``parseHexInt`` and ``parseOctInt`` now fail on empty strings
and strings containing only valid prefixes, e.g. "0x" for hex integers.
- ``terminal.setCursorPos`` and ``terminal.setCursorXPos`` now work correctly
with 0-based coordinates on POSIX (previously, you needed to use
1-based coordinates on POSIX for correct behaviour; the Windows behaviour
was always correct).
#### Breaking changes in the compiler

View File

@@ -322,7 +322,8 @@ const
usesEffects* = 1 # read effects at position 1
writeEffects* = 2 # write effects at position 2
tagEffects* = 3 # user defined tag ('gc', 'time' etc.)
effectListLen* = 4 # list of effects list
pragmasEffects* = 4 # not an effect, but a slot for pragmas in proc type
effectListLen* = 5 # list of effects list
type
TTypeKind* = enum # order is important!

View File

@@ -27,9 +27,9 @@ proc lineInfoToStr*(conf: ConfigRef; info: TLineInfo): Rope
when declared(echo):
# these are for debugging only: They are not really deprecated, but I want
# the warning so that release versions do not contain debugging statements:
proc debug*(n: PSym; conf: ConfigRef = nil) {.deprecated.}
proc debug*(n: PType; conf: ConfigRef = nil) {.deprecated.}
proc debug*(n: PNode; conf: ConfigRef = nil) {.deprecated.}
proc debug*(n: PSym; conf: ConfigRef = nil) {.exportc: "debugSym", deprecated.}
proc debug*(n: PType; conf: ConfigRef = nil) {.exportc: "debugType", deprecated.}
proc debug*(n: PNode; conf: ConfigRef = nil) {.exportc: "debugNode", deprecated.}
template debug*(x: PSym|PType|PNode) {.deprecated.} =
when compiles(c.config):

View File

@@ -255,16 +255,14 @@ proc genGenericAsgn(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
# here for this flag, where it is reasonably safe to do so
# (for objects, etc.):
if p.config.selectedGC == gcDestructors:
useStringh(p.module)
linefmt(p, cpsStmts,
"memcpy((void*)$1, (NIM_CONST void*)$2, sizeof($3));$n",
addrLoc(p.config, dest), addrLoc(p.config, src), rdLoc(dest))
"#nimCopyMem((void*)$1, (NIM_CONST void*)$2, sizeof($3));$n",
addrLoc(p.config, dest), addrLoc(p.config, src), rdLoc(dest))
elif needToCopy notin flags or
tfShallow in skipTypes(dest.t, abstractVarRange).flags:
if dest.storage == OnStack or not usesWriteBarrier(p.config):
useStringh(p.module)
linefmt(p, cpsStmts,
"memcpy((void*)$1, (NIM_CONST void*)$2, sizeof($3));$n",
"#nimCopyMem((void*)$1, (NIM_CONST void*)$2, sizeof($3));$n",
addrLoc(p.config, dest), addrLoc(p.config, src), rdLoc(dest))
else:
linefmt(p, cpsStmts, "#genericShallowAssign((void*)$1, (void*)$2, $3);$n",
@@ -345,9 +343,8 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
if needsComplexAssignment(dest.t):
genGenericAsgn(p, dest, src, flags)
else:
useStringh(p.module)
linefmt(p, cpsStmts,
"memcpy((void*)$1, (NIM_CONST void*)$2, sizeof($3));$n",
"#nimCopyMem((void*)$1, (NIM_CONST void*)$2, sizeof($3));$n",
rdLoc(dest), rdLoc(src), getTypeDesc(p.module, dest.t))
of tyOpenArray, tyVarargs:
# open arrays are always on the stack - really? What if a sequence is
@@ -358,16 +355,14 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
addrLoc(p.config, dest), addrLoc(p.config, src),
genTypeInfo(p.module, dest.t, dest.lode.info))
else:
useStringh(p.module)
linefmt(p, cpsStmts,
# bug #4799, keep the memcpy for a while
#"memcpy((void*)$1, (NIM_CONST void*)$2, sizeof($1[0])*$1Len_0);$n",
# bug #4799, keep the nimCopyMem for a while
#"#nimCopyMem((void*)$1, (NIM_CONST void*)$2, sizeof($1[0])*$1Len_0);$n",
"$1 = $2;$n",
rdLoc(dest), rdLoc(src))
of tySet:
if mapType(p.config, ty) == ctArray:
useStringh(p.module)
linefmt(p, cpsStmts, "memcpy((void*)$1, (NIM_CONST void*)$2, $3);$n",
linefmt(p, cpsStmts, "#nimCopyMem((void*)$1, (NIM_CONST void*)$2, $3);$n",
rdLoc(dest), rdLoc(src), rope(getSize(p.config, dest.t)))
else:
linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src))
@@ -412,8 +407,7 @@ proc genDeepCopy(p: BProc; dest, src: TLoc) =
genTypeInfo(p.module, dest.t, dest.lode.info))
of tySet:
if mapType(p.config, ty) == ctArray:
useStringh(p.module)
linefmt(p, cpsStmts, "memcpy((void*)$1, (NIM_CONST void*)$2, $3);$n",
linefmt(p, cpsStmts, "#nimCopyMem((void*)$1, (NIM_CONST void*)$2, $3);$n",
rdLoc(dest), rdLoc(src), rope(getSize(p.config, dest.t)))
else:
linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src))
@@ -1515,9 +1509,8 @@ proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
if op == mHigh: unaryExpr(p, e, d, "($1Len_0-1)")
else: unaryExpr(p, e, d, "$1Len_0")
of tyCString:
useStringh(p.module)
if op == mHigh: unaryExpr(p, e, d, "($1 ? (strlen($1)-1) : -1)")
else: unaryExpr(p, e, d, "($1 ? strlen($1) : 0)")
if op == mHigh: unaryExpr(p, e, d, "($1 ? (#nimCStrLen($1)-1) : -1)")
else: unaryExpr(p, e, d, "($1 ? #nimCStrLen($1) : 0)")
of tyString:
if not p.module.compileToCpp:
if op == mHigh: unaryExpr(p, e, d, "($1 ? ($1->Sup.len-1) : -1)")
@@ -1672,7 +1665,7 @@ proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
" $3 = (($4[$1] & ~ $5[$1]) == 0);$n" &
" if (!$3) break;}$n", "for ($1 = 0; $1 < $2; $1++) { $n" &
" $3 = (($4[$1] & ~ $5[$1]) == 0);$n" & " if (!$3) break;}$n" &
"if ($3) $3 = (memcmp($4, $5, $2) != 0);$n",
"if ($3) $3 = (#nimCmpMem($4, $5, $2) != 0);$n",
"&", "|", "& ~", "^"]
var a, b, i: TLoc
var setType = skipTypes(e.sons[1].typ, abstractVar)
@@ -1711,11 +1704,10 @@ proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
initLocExpr(p, e.sons[1], a)
initLocExpr(p, e.sons[2], b)
if d.k == locNone: getTemp(p, getSysType(p.module.g.graph, unknownLineInfo(), tyBool), d)
lineF(p, cpsStmts, lookupOpr[op],
linefmt(p, cpsStmts, lookupOpr[op],
[rdLoc(i), rope(size), rdLoc(d), rdLoc(a), rdLoc(b)])
of mEqSet:
useStringh(p.module)
binaryExprChar(p, e, d, "(memcmp($1, $2, " & $(size) & ")==0)")
binaryExprChar(p, e, d, "(#nimCmpMem($1, $2, " & $(size) & ")==0)")
of mMulSet, mPlusSet, mMinusSet, mSymDiffSet:
# we inline the simple for loop for better code generation:
getTemp(p, getSysType(p.module.g.graph, unknownLineInfo(), tyInt), i) # our counter
@@ -1979,7 +1971,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
proc genSetConstr(p: BProc, e: PNode, d: var TLoc) =
# example: { a..b, c, d, e, f..g }
# we have to emit an expression of the form:
# memset(tmp, 0, sizeof(tmp)); inclRange(tmp, a, b); incl(tmp, c);
# nimZeroMem(tmp, sizeof(tmp)); inclRange(tmp, a, b); incl(tmp, c);
# incl(tmp, d); incl(tmp, e); inclRange(tmp, f, g);
var
a, b, idx: TLoc
@@ -1989,8 +1981,7 @@ proc genSetConstr(p: BProc, e: PNode, d: var TLoc) =
if d.k == locNone: getTemp(p, e.typ, d)
if getSize(p.config, e.typ) > 8:
# big set:
useStringh(p.module)
lineF(p, cpsStmts, "memset($1, 0, sizeof($2));$n",
linefmt(p, cpsStmts, "#nimZeroMem($1, sizeof($2));$n",
[rdLoc(d), getTypeDesc(p.module, e.typ)])
for it in e.sons:
if it.kind == nkRange:

View File

@@ -1146,5 +1146,9 @@ proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) =
proc genStmts(p: BProc, t: PNode) =
var a: TLoc
let isPush = hintExtendedContext in p.config.notes
if isPush: pushInfoContext(p.config, t.info)
expr(p, t, a)
if isPush: popInfoContext(p.config)
internalAssert p.config, a.k in {locNone, locTemp, locLocalVar}

View File

@@ -48,7 +48,7 @@ proc mangleName(m: BModule; s: PSym): Rope =
result = s.loc.r
if result == nil:
result = s.name.s.mangle.rope
add(result, idOrSig(s, m.module.name.s, m.sigConflicts))
add(result, idOrSig(s, m.module.name.s.mangle, m.sigConflicts))
s.loc.r = result
writeMangledName(m.ndi, s, m.config)

View File

@@ -80,11 +80,6 @@ proc isSimpleConst(typ: PType): bool =
{tyTuple, tyObject, tyArray, tySet, tySequence} and not
(t.kind == tyProc and t.callConv == ccClosure)
proc useStringh(m: BModule) =
if includesStringh notin m.flags:
incl m.flags, includesStringh
m.includeHeader("<string.h>")
proc useHeader(m: BModule, sym: PSym) =
if lfHeader in sym.loc.flags:
assert(sym.annex != nil)
@@ -329,10 +324,9 @@ proc resetLoc(p: BProc, loc: var TLoc) =
# field, so disabling this should be safe:
genObjectInit(p, cpsStmts, loc.t, loc, true)
else:
useStringh(p.module)
# array passed as argument decayed into pointer, bug #7332
# so we use getTypeDesc here rather than rdLoc(loc)
linefmt(p, cpsStmts, "memset((void*)$1, 0, sizeof($2));$n",
linefmt(p, cpsStmts, "#nimZeroMem((void*)$1, sizeof($2));$n",
addrLoc(p.config, loc), getTypeDesc(p.module, loc.t))
# XXX: We can be extra clever here and call memset only
# on the bytes following the m_type field?
@@ -345,11 +339,10 @@ proc constructLoc(p: BProc, loc: TLoc, isTemp = false) =
getTypeDesc(p.module, typ))
else:
if not isTemp or containsGarbageCollectedRef(loc.t):
# don't use memset for temporary values for performance if we can
# don't use nimZeroMem for temporary values for performance if we can
# avoid it:
if not isImportedCppType(typ):
useStringh(p.module)
linefmt(p, cpsStmts, "memset((void*)$1, 0, sizeof($2));$n",
linefmt(p, cpsStmts, "#nimZeroMem((void*)$1, sizeof($2));$n",
addrLoc(p.config, loc), getTypeDesc(p.module, typ))
genObjectInit(p, cpsStmts, loc.t, loc, true)
@@ -680,6 +673,7 @@ proc generateHeaders(m: BModule) =
add(m.s[cfsHeaders], "#undef linux\L")
add(m.s[cfsHeaders], "#undef mips\L")
add(m.s[cfsHeaders], "#undef near\L")
add(m.s[cfsHeaders], "#undef far\L")
add(m.s[cfsHeaders], "#undef powerpc\L")
add(m.s[cfsHeaders], "#undef unix\L")

View File

@@ -16,7 +16,7 @@ import
wordrecg, syntaxes, renderer, lexer, packages/docutils/rstast,
packages/docutils/rst, packages/docutils/rstgen, times,
packages/docutils/highlite, sempass2, json, xmltree, cgi,
typesrenderer, astalgo, modulepaths, lineinfos
typesrenderer, astalgo, modulepaths, lineinfos, sequtils
type
TSections = array[TSymKind, Rope]
@@ -259,11 +259,19 @@ proc nodeToHighlightedHtml(d: PDoc; n: PNode; result: var Rope; renderFlags: TRe
of tkSpaces, tkInvalid:
add(result, literal)
of tkCurlyDotLe:
dispA(d.conf, result, """<span class="Other pragmabegin">$1</span><div class="pragma">""",
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">
<span class="Other">$1</span>
<span class="pragma">""".replace("\n", ""), # Must remove newlines because wrapped in a <pre>
"\\spanOther{$1}",
[rope(esc(d.target, literal))])
of tkCurlyDotRi:
dispA(d.conf, result, "</div><span class=\"Other pragmaend\">$1</span>",
dispA(d.conf, result, """
</span>
<span class="Other">$1</span>
</span>""".replace("\n", ""),
"\\spanOther{$1}",
[rope(esc(d.target, literal))])
of tkParLe, tkParRi, tkBracketLe, tkBracketRi, tkCurlyLe, tkCurlyRi,
@@ -280,7 +288,7 @@ proc getAllRunnableExamples(d: PDoc; n: PNode; dest: var Rope) =
of nkCallKinds:
if n[0].kind == nkSym and n[0].sym.magic == mRunnableExamples and
n.len >= 2 and n.lastSon.kind == nkStmtList:
dispA(d.conf, dest, "\n<strong class=\"examples_text\">$1</strong>\n",
dispA(d.conf, dest, "\n<p><strong class=\"examples_text\">$1</strong></p>\n",
"\n\\textbf{$1}\n", [rope"Examples:"])
inc d.listingCounter
let id = $d.listingCounter

View File

@@ -434,10 +434,7 @@ proc completeCFilePath*(conf: ConfigRef; cfile: string, createSubDir: bool = tru
proc toObjFile*(conf: ConfigRef; filename: string): string =
# Object file for compilation
#if filename.endsWith(".cpp"):
# result = changeFileExt(filename, "cpp." & CC[cCompiler].objExt)
#else:
result = changeFileExt(filename, CC[conf.cCompiler].objExt)
result = filename & "." & CC[conf.cCompiler].objExt
proc addFileToCompile*(conf: ConfigRef; cf: Cfile) =
conf.toCompile.add(cf)

View File

@@ -45,7 +45,8 @@ type
hintExecuting, hintLinking, hintDependency,
hintSource, hintPerformance, hintStackTrace, hintGCStats,
hintGlobalVar,
hintUser, hintUserRaw
hintUser, hintUserRaw,
hintExtendedContext
const
MsgKindToStr*: array[TMsgKind, string] = [
@@ -116,7 +117,9 @@ const
hintGCStats: "$1",
hintGlobalVar: "global variable declared here",
hintUser: "$1",
hintUserRaw: "$1"]
hintUserRaw: "$1",
hintExtendedContext: "$1",
]
const
WarningsToStr* = ["CannotOpenFile", "OctalEscape",
@@ -132,12 +135,14 @@ const
"GcMem", "Destructor", "LockLevel", "ResultShadowed",
"Spacing", "User"]
HintsToStr* = ["Success", "SuccessX", "LineTooLong",
HintsToStr* = [
"Success", "SuccessX", "LineTooLong",
"XDeclaredButNotUsed", "ConvToBaseNotNeeded", "ConvFromXtoItselfNotNeeded",
"ExprAlwaysX", "QuitCalled", "Processing", "CodeBegin", "CodeEnd", "Conf",
"Path", "CondTrue", "Name", "Pattern", "Exec", "Link", "Dependency",
"Source", "Performance", "StackTrace", "GCStats", "GlobalVar",
"User", "UserRaw"]
"User", "UserRaw", "ExtendedContext",
]
const
fatalMin* = errUnknown
@@ -157,30 +162,17 @@ type
TNoteKind* = range[warnMin..hintMax] # "notes" are warnings or hints
TNoteKinds* = set[TNoteKind]
const
NotesVerbosity*: array[0..3, TNoteKinds] = [
{low(TNoteKind)..high(TNoteKind)} - {warnShadowIdent, warnUninit,
warnProveField, warnProveIndex,
warnGcUnsafe,
hintSuccessX, hintPath, hintConf,
hintProcessing, hintPattern,
hintDependency,
hintExecuting, hintLinking,
hintCodeBegin, hintCodeEnd,
hintSource, hintStackTrace,
hintGlobalVar, hintGCStats},
{low(TNoteKind)..high(TNoteKind)} - {warnShadowIdent, warnUninit,
warnProveField, warnProveIndex,
warnGcUnsafe,
hintPath,
hintDependency,
hintCodeBegin, hintCodeEnd,
hintSource, hintStackTrace,
hintGlobalVar, hintGCStats},
{low(TNoteKind)..high(TNoteKind)} - {hintStackTrace, warnUninit},
{low(TNoteKind)..high(TNoteKind)}]
proc computeNotesVerbosity(): array[0..3, TNoteKinds] =
result[3] = {low(TNoteKind)..high(TNoteKind)} - {}
result[2] = result[3] - {hintStackTrace, warnUninit, hintExtendedContext}
result[1] = result[2] - {warnShadowIdent, warnProveField, warnProveIndex,
warnGcUnsafe, hintPath, hintDependency, hintCodeBegin, hintCodeEnd,
hintSource, hintGlobalVar, hintGCStats}
result[0] = result[1] - {hintSuccessX, hintConf, hintProcessing,
hintPattern, hintExecuting, hintLinking}
const
NotesVerbosity* = computeNotesVerbosity()
errXMustBeCompileTime* = "'$1' can only be used in compile-time context"
errArgsNeedRunOption* = "arguments can only be given if the '--run' option is selected"

View File

@@ -189,7 +189,7 @@ proc putComment(g: var TSrcGen, s: string) =
put(g, tkComment, com)
com = "## "
inc(i)
if i < s.len and s[i] == '\x0A': inc(i)
if i <= hi and s[i] == '\x0A': inc(i)
optNL(g, ind)
of '\x0A':
put(g, tkComment, com)
@@ -226,7 +226,7 @@ proc maxLineLength(s: string): int =
break
of '\x0D':
inc(i)
if s[i] == '\x0A': inc(i)
if i <= hi and s[i] == '\x0A': inc(i)
result = max(result, lineLen)
lineLen = 0
of '\x0A':
@@ -247,7 +247,7 @@ proc putRawStr(g: var TSrcGen, kind: TTokType, s: string) =
put(g, kind, str)
str = ""
inc(i)
if (i <= hi) and (s[i] == '\x0A'): inc(i)
if i <= hi and s[i] == '\x0A': inc(i)
optNL(g, 0)
of '\x0A':
put(g, kind, str)
@@ -416,7 +416,7 @@ proc lsub(g: TSrcGen; n: PNode): int =
of nkCast: result = lsub(g, n.sons[0]) + lsub(g, n.sons[1]) + len("cast[]()")
of nkAddr: result = (if n.len>0: lsub(g, n.sons[0]) + len("addr()") else: 4)
of nkStaticExpr: result = lsub(g, n.sons[0]) + len("static_")
of nkHiddenAddr, nkHiddenDeref: result = lsub(g, n.sons[0])
of nkHiddenAddr, nkHiddenDeref, nkStringToCString, nkCStringToString: result = lsub(g, n.sons[0])
of nkCommand: result = lsub(g, n.sons[0]) + lcomma(g, n, 1) + 1
of nkExprEqExpr, nkAsgn, nkFastAsgn: result = lsons(g, n) + 3
of nkPar, nkCurly, nkBracket, nkClosure: result = lcomma(g, n) + 2
@@ -446,7 +446,7 @@ proc lsub(g: TSrcGen; n: PNode): int =
of nkChckRangeF: result = len("chckRangeF") + 2 + lcomma(g, n)
of nkChckRange64: result = len("chckRange64") + 2 + lcomma(g, n)
of nkChckRange: result = len("chckRange") + 2 + lcomma(g, n)
of nkObjDownConv, nkObjUpConv, nkStringToCString, nkCStringToString:
of nkObjDownConv, nkObjUpConv:
result = 2
if sonsLen(n) >= 1: result = result + lsub(g, n.sons[0])
result = result + lcomma(g, n, 1)
@@ -968,7 +968,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
put(g, tkParLe, "(")
gcomma(g, n)
put(g, tkParRi, ")")
of nkObjDownConv, nkObjUpConv, nkStringToCString, nkCStringToString:
of nkObjDownConv, nkObjUpConv:
if sonsLen(n) >= 1: gsub(g, n.sons[0])
put(g, tkParLe, "(")
gcomma(g, n, 1)
@@ -1020,7 +1020,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
of nkBind:
putWithSpace(g, tkBind, "bind")
gsub(g, n, 0)
of nkCheckedFieldExpr, nkHiddenAddr, nkHiddenDeref:
of nkCheckedFieldExpr, nkHiddenAddr, nkHiddenDeref, nkStringToCString, nkCStringToString:
gsub(g, n, 0)
of nkLambda:
putWithSpace(g, tkProc, "proc")
@@ -1073,9 +1073,13 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
elif n[0].kind == nkSym: n[0].sym.name
elif n[0].kind in {nkOpenSymChoice, nkClosedSymChoice}: n[0][0].sym.name
else: nil
if n[1].kind == nkPrefix or (opr != nil and renderer.isKeyword(opr)):
var n_next = n[1]
while n_next.kind in {nkCheckedFieldExpr, nkHiddenAddr, nkHiddenDeref,
nkStringToCString, nkCStringToString} and n_next.len > 0:
n_next = n_next[0]
if n_next.kind == nkPrefix or (opr != nil and renderer.isKeyword(opr)):
put(g, tkSpaces, Space)
if n.sons[1].kind == nkInfix:
if n_next.kind == nkInfix:
put(g, tkParLe, "(")
gsub(g, n.sons[1])
put(g, tkParRi, ")")

View File

@@ -610,14 +610,21 @@ proc myProcess(context: PPassContext, n: PNode): PNode =
proc testExamples(c: PContext) =
let inp = toFullPath(c.config, c.module.info)
let outp = inp.changeFileExt"" & "_examples.nim"
let nimcache = outp.changeFileExt"" & "_nimcache"
renderModule(c.runnableExamples, inp, outp)
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 & " -r " & outp) != 0:
if os.execShellCmd(os.getAppFilename() & " " & backend & " --nimcache:" & nimcache & " -r " & outp) != 0:
quit "[Examples] failed"
removeFile(outp)
else:
removeFile(outp)
removeFile(outp.changeFileExt(ExeExt))
try:
removeDir(nimcache)
except OSError:
discard
proc myClose(graph: ModuleGraph; context: PPassContext, n: PNode): PNode =
var c = PContext(context)

View File

@@ -907,8 +907,11 @@ proc buildEchoStmt(c: PContext, n: PNode): PNode =
result = semExpr(c, result)
proc semExprNoType(c: PContext, n: PNode): PNode =
let isPush = hintExtendedContext in c.config.notes
if isPush: pushInfoContext(c.config, n.info)
result = semExpr(c, n, {efWantStmt})
discardCheck(c, result)
if isPush: popInfoContext(c.config)
proc isTypeExpr(n: PNode): bool =
case n.kind

View File

@@ -171,7 +171,7 @@ proc semTypeTraits(c: PContext, n: PNode): PNode =
proc semOrd(c: PContext, n: PNode): PNode =
result = n
let parType = n.sons[1].typ
if isOrdinalType(parType):
if isOrdinalType(parType, allowEnumWithHoles=true):
discard
elif parType.kind == tySet:
result.typ = makeRangeType(c, firstOrd(c.config, parType), lastOrd(c.config, parType), n.info)

View File

@@ -552,6 +552,10 @@ proc isOwnedProcVar(n: PNode; owner: PSym): bool =
# XXX prove the soundness of this effect system rule
result = n.kind == nkSym and n.sym.kind == skParam and owner == n.sym.owner
proc isNoEffectList(n: PNode): bool {.inline.} =
assert n.kind == nkEffectList
n.len == 0 or (n[tagEffects] == nil and n[exceptionEffects] == nil)
proc trackOperand(tracked: PEffects, n: PNode, paramType: PType) =
let a = skipConvAndClosure(n)
let op = a.typ
@@ -561,7 +565,7 @@ proc trackOperand(tracked: PEffects, n: PNode, paramType: PType) =
let s = n.skipConv
if s.kind == nkSym and s.sym.kind in routineKinds:
propagateEffects(tracked, n, s.sym)
elif effectList.len == 0:
elif isNoEffectList(effectList):
if isForwardedProc(n):
# we have no explicit effects but it's a forward declaration and so it's
# stated there are no additional effects, so simply propagate them:
@@ -723,7 +727,7 @@ proc track(tracked: PEffects, n: PNode) =
var effectList = op.n.sons[0]
if a.kind == nkSym and a.sym.kind == skMethod:
propagateEffects(tracked, n, a.sym)
elif effectList.len == 0:
elif isNoEffectList(effectList):
if isForwardedProc(a):
propagateEffects(tracked, n, a.sym)
elif isIndirectCall(a, tracked.owner):
@@ -781,6 +785,15 @@ proc track(tracked: PEffects, n: PNode) =
initVar(tracked, child.sons[i], volatileCheck=false)
addAsgnFact(tracked.guards, child.sons[i], last)
notNilCheck(tracked, last, child.sons[i].typ)
elif child.kind == nkVarTuple and last.kind != nkEmpty:
for i in 0 .. child.len-2:
if child[i].kind == nkEmpty or
child[i].kind == nkSym and child[i].sym.name.s == "_":
continue
initVar(tracked, child[i], volatileCheck=false)
if last.kind in {nkPar, nkTupleConstr}:
addAsgnFact(tracked.guards, child[i], last[i])
notNilCheck(tracked, last[i], child[i].typ)
# since 'var (a, b): T = ()' is not even allowed, there is always type
# inference for (a, b) and thus no nil checking is necessary.
of nkConstSection:
@@ -897,19 +910,18 @@ proc checkMethodEffects*(g: ModuleGraph; disp, branch: PSym) =
[$branch.typ.lockLevel, $disp.typ.lockLevel])
proc setEffectsForProcType*(g: ModuleGraph; t: PType, n: PNode) =
var effects = t.n.sons[0]
var effects = t.n[0]
if t.kind != tyProc or effects.kind != nkEffectList: return
let
raisesSpec = effectSpec(n, wRaises)
tagsSpec = effectSpec(n, wTags)
if not isNil(raisesSpec) or not isNil(tagsSpec):
if n.kind != nkEmpty:
internalAssert g.config, effects.len == 0
newSeq(effects.sons, effectListLen)
let raisesSpec = effectSpec(n, wRaises)
if not isNil(raisesSpec):
effects.sons[exceptionEffects] = raisesSpec
effects[exceptionEffects] = raisesSpec
let tagsSpec = effectSpec(n, wTags)
if not isNil(tagsSpec):
effects.sons[tagEffects] = tagsSpec
effects[tagEffects] = tagsSpec
effects[pragmasEffects] = n
proc initEffects(g: ModuleGraph; effects: PNode; s: PSym; t: var TEffects) =
newSeq(effects.sons, effectListLen)
@@ -917,6 +929,7 @@ proc initEffects(g: ModuleGraph; effects: PNode; s: PSym; t: var TEffects) =
effects.sons[tagEffects] = newNodeI(nkArgList, s.info)
effects.sons[usesEffects] = g.emptyNode
effects.sons[writeEffects] = g.emptyNode
effects.sons[pragmasEffects] = g.emptyNode
t.exc = effects.sons[exceptionEffects]
t.tags = effects.sons[tagEffects]

View File

@@ -557,7 +557,10 @@ proc semCaseBranch(c: PContext, t, branch: PNode, branchIndex: int,
return
elif r.kind notin {nkCurly, nkBracket} or len(r) == 0:
checkMinSonsLen(t, 1, c.config)
branch.sons[i] = skipConv(fitNode(c, t.sons[0].typ, r, r.info))
var tmp = fitNode(c, t.sons[0].typ, r, r.info)
# the call to fitNode may introduce a call to a converter
if tmp.kind in {nkHiddenCallConv}: tmp = semConstExpr(c, tmp)
branch.sons[i] = skipConv(tmp)
inc(covered)
else:
if r.kind == nkCurly:
@@ -1341,6 +1344,10 @@ proc semTypeClass(c: PContext, n: PNode, prev: PType): PType =
dummyName = param
dummyType = candidateTypeSlot
# this can be true for 'nim check' on incomplete concepts,
# see bug #8230
if dummyName.kind == nkEmpty: continue
internalAssert c.config, dummyName.kind == nkIdent
var dummyParam = newSym(if modifier == tyTypeDesc: skType else: skVar,
dummyName.ident, owner, param.info)

View File

@@ -163,7 +163,7 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]) =
c.hashType t.sons[i], flags
else:
c.hashType t.lastSon, flags
of tyAlias, tySink, tyUserTypeClasses:
of tyAlias, tySink, tyUserTypeClasses, tyInferred:
c.hashType t.lastSon, flags
of tyBool, tyChar, tyInt..tyUInt64:
# no canonicalization for integral types, so that e.g. ``pid_t`` is

View File

@@ -569,7 +569,10 @@ proc procParamTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
# signature. There is a change that the target
# type is already fully-determined, so we are
# going to try resolve it
f = generateTypeInstance(c.c, c.bindings, c.call.info, f)
if c.call != nil:
f = generateTypeInstance(c.c, c.bindings, c.call.info, f)
else:
f = nil
if f == nil or f.isMetaType:
# no luck resolving the type, so the inference fails
return isBothMetaConvertible
@@ -637,7 +640,7 @@ proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
else: discard
proc typeRangeRel(f, a: PType): TTypeRelation {.noinline.} =
template checkRange[T](a0, a1, f0, f1: T): TTypeRelation =
template checkRange[T](a0, a1, f0, f1: T): TTypeRelation =
if a0 == f0 and a1 == f1:
isEqual
elif a0 >= f0 and a1 <= f1:
@@ -647,12 +650,12 @@ proc typeRangeRel(f, a: PType): TTypeRelation {.noinline.} =
isConvertible
else:
isNone
if f.isOrdinalType:
if f.isOrdinalType:
checkRange(firstOrd(nil, a), lastOrd(nil, a), firstOrd(nil, f), lastOrd(nil, f))
else:
else:
checkRange(firstFloat(a), lastFloat(a), firstFloat(f), lastFloat(f))
proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType =
var
@@ -1801,7 +1804,7 @@ proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType,
# 'f <- dest' in order to not break the unification:
# see tests/tgenericconverter:
let srca = typeRel(m, src, a)
if srca notin {isEqual, isGeneric}: continue
if srca notin {isEqual, isGeneric, isSubtype}: continue
let destIsGeneric = containsGenericType(dest)
if destIsGeneric:
@@ -1814,7 +1817,12 @@ proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType,
s.info = arg.info
result = newNodeIT(nkHiddenCallConv, arg.info, dest)
addSon(result, s)
addSon(result, copyTree(arg))
var param: PNode = nil
if srca == isSubtype:
param = implicitConv(nkHiddenSubConv, src, copyTree(arg), m, c)
else:
param = copyTree(arg)
addSon(result, param)
inc(m.convMatches)
m.genericConverter = srca == isGeneric or destIsGeneric
return result
@@ -2231,6 +2239,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
m.state = csNoMatch
return
m.baseTypeMatch = false
m.typedescMatched = false
n.sons[a].sons[1] = prepareOperand(c, formal.typ, n.sons[a].sons[1])
n.sons[a].typ = n.sons[a].sons[1].typ
var arg = paramTypesMatch(m, formal.typ, n.sons[a].typ,
@@ -2266,6 +2275,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
# beware of the side-effects in 'prepareOperand'! So only do it for
# varargs matching. See tests/metatype/tstatic_overloading.
m.baseTypeMatch = false
m.typedescMatched = false
incl(marker, formal.position)
n.sons[a] = prepareOperand(c, formal.typ, n.sons[a])
var arg = paramTypesMatch(m, formal.typ, n.sons[a].typ,
@@ -2300,6 +2310,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
addSon(container, n.sons[a])
else:
m.baseTypeMatch = false
m.typedescMatched = false
n.sons[a] = prepareOperand(c, formal.typ, n.sons[a])
var arg = paramTypesMatch(m, formal.typ, n.sons[a].typ,
n.sons[a], nOrig.sons[a])

View File

@@ -133,18 +133,18 @@ proc elemType*(t: PType): PType =
else: result = t.lastSon
assert(result != nil)
proc isOrdinalType*(t: PType): bool =
proc enumHasHoles*(t: PType): bool =
var b = t.skipTypes({tyRange, tyGenericInst, tyAlias, tySink})
result = b.kind == tyEnum and tfEnumHasHoles in b.flags
proc isOrdinalType*(t: PType, allowEnumWithHoles = false): bool =
assert(t != nil)
const
# caution: uint, uint64 are no ordinal types!
baseKinds = {tyChar,tyInt..tyInt64,tyUInt8..tyUInt32,tyBool,tyEnum}
parentKinds = {tyRange, tyOrdinal, tyGenericInst, tyAlias, tySink, tyDistinct}
t.kind in baseKinds or (t.kind in parentKinds and isOrdinalType(t.sons[0]))
proc enumHasHoles*(t: PType): bool =
var b = t
while b.kind in {tyRange, tyGenericInst, tyAlias, tySink}: b = b.sons[0]
result = b.kind == tyEnum and tfEnumHasHoles in b.flags
(t.kind in baseKinds and not (t.enumHasHoles and not allowEnumWithHoles)) or
(t.kind in parentKinds and isOrdinalType(t.lastSon))
proc iterOverTypeAux(marker: var IntSet, t: PType, iter: TTypeIter,
closure: RootRef): bool

View File

@@ -533,8 +533,10 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
let s = regs[rb].node.strVal
if s.isNil:
stackTrace(c, tos, pc, errNilAccess)
elif idx <=% s.len:
elif idx <% s.len:
regs[ra].intVal = s[idx].ord
elif idx == s.len and optLaxStrings in c.config.options:
regs[ra].intVal = 0
else:
stackTrace(c, tos, pc, errIndexOutOfBounds)
of opcWrArr:

View File

@@ -229,7 +229,8 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo;
for i in 1..<t.sons.len:
fp.add newIdentDefs(t.n[i], t.sons[i])
result.add fp
result.add newNodeI(nkEmpty, info) # pragmas aren't reconstructed yet
result.add if t.n[0].len > 0: t.n[0][pragmasEffects].copyTree
else: newNodeI(nkEmpty, info)
else:
result = mapTypeToBracket("proc", mNone, t, info)
of tyOpenArray: result = mapTypeToBracket("openArray", mOpenArray, t, info)

View File

@@ -111,10 +111,10 @@ path="$lib/pure"
@if nintendoswitch:
cc = "switch_gcc"
switch_gcc.options.linker = "-g -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE $SWITCH_LIBS"
switch_gcc.cpp.options.linker = "-g -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE $SWITCH_LIBS"
switch_gcc.options.always = "-g -Wall -O2 -ffunction-sections -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE $SWITCH_INCLUDES -D__SWITCH__"
switch_gcc.cpp.options.always = "-g -Wall -O2 -ffunction-sections -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE $SWITCH_INCLUDES -D__SWITCH__ -fno-rtti -fno-exceptions -std=gnu++11"
switch_gcc.options.linker = "-g -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE"
switch_gcc.cpp.options.linker = "-g -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE"
switch_gcc.options.always = "-g -Wall -O2 -ffunction-sections -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE -D__SWITCH__"
switch_gcc.cpp.options.always = "-g -Wall -O2 -ffunction-sections -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE -D__SWITCH__ -fno-rtti -fno-exceptions -std=gnu++11"
@end
# Configuration for the Intel C/C++ compiler:

View File

@@ -1330,15 +1330,6 @@ dt pre > span.Operator ~ span.Identifier {
background-repeat: no-repeat;
background-image: url("data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA==");
margin-bottom: -5px; }
div.pragma {
display: none;
}
span.pragmabegin {
cursor: pointer;
}
span.pragmaend {
cursor: pointer;
}
div.search_results {
background-color: antiquewhite;
@@ -1351,32 +1342,47 @@ div#global-links ul {
margin-left: 0;
list-style-type: none;
}
span.pragmadots {
/* Position: relative frees us up to make the dots
look really nice without fucking up the layout and
causing bulging in the parent container */
position: relative;
/* 1px down looks slightly nicer */
top: 1px;
padding: 2px;
background-color: #D3D3D3;
border-radius: 4px;
margin: 0 2px;
cursor: pointer;
/* For some reason on Chrome, making the font size
smaller than 1em is causing the parent container to
bulge slightly. So, we're stuck with inheriting 1em,
which is sad, because 0.8em looks better... */
}
span.pragmadots:hover {
background-color: #DBDBDB;
}
span.pragmawrap {
display: none;
}
</style>
<script type="text/javascript" src="../dochack.js"></script>
<script type="text/javascript">
function togglepragma(d) {
if (d.style.display != 'inline')
d.style.display = 'inline';
else
d.style.display = 'none';
}
function main() {
var elements = document.getElementsByClassName("pragmabegin");
for (var i = 0; i < elements.length; ++i) {
var e = elements[i];
e.onclick = function(event) {
togglepragma(event.target.nextSibling);
};
}
var elements = document.getElementsByClassName("pragmaend");
for (var i = 0; i < elements.length; ++i) {
var e = elements[i];
e.onclick = function(event) {
togglepragma(event.target.previousSibling);
};
var pragmaDots = document.getElementsByClassName("pragmadots");
for (var i = 0; i < pragmaDots.length; i++) {
pragmaDots[i].onclick = function(event) {
// Hide tease
event.target.parentNode.style.display = "none";
// Show actual
event.target.parentNode.nextElementSibling.style.display = "inline";
}
}
}
</script>

View File

@@ -1270,10 +1270,10 @@ AST:
nnkIdent("float32"),
nnkEmpty()
)
nnkPragma(nnkIdent("inline")),
nnkEmpty(), # reserved slot for future use
nnkStmtList(nnkDiscardStmt(nnkEmpty())) # the meat of the proc
)
),
nnkPragma(nnkIdent("inline")),
nnkEmpty(), # reserved slot for future use
nnkStmtList(nnkDiscardStmt(nnkEmpty())) # the meat of the proc
)
There is another consideration. Nim has flexible type identification for

View File

@@ -12,7 +12,8 @@ Options:
-p, --path:PATH add path to search paths
-d, --define:SYMBOL(:VAL)
define a conditional symbol
(Optionally: Define the value for that symbol)
(Optionally: Define the value for that symbol,
see: "compile time define pragmas")
-u, --undef:SYMBOL undefine a conditional symbol
-f, --forceBuild force rebuilding of all modules
--stackTrace:on|off turn stack tracing on|off

View File

@@ -253,7 +253,7 @@ the exact spelling of an identifier. The exception with respect to the first
letter allows common code like ``var foo: Foo`` to be parsed unambiguously.
Historically, Nim was a fully `style-insensitive`:idx: language. This meant that
it was not case-sensitive and underscores were ignored and there was no even a
it was not case-sensitive and underscores were ignored and there was not even a
distinction between ``foo`` and ``Foo``.

View File

@@ -232,12 +232,12 @@ Cross compilation for Nintendo Switch
=====================================
Simply add --os:nintendoswitch
to your usual ``nim c`` or ``nim cpp`` command and set the ``SWITCH_LIBS``
and ``SWITCH_INCLUDES`` environment variables to something like:
to your usual ``nim c`` or ``nim cpp`` command and set the ``passC``
and ``passL`` command line switches to something like:
.. code-block:: console
export SWITCH_INCLUDES="-I$DEVKITPRO/libnx/include"
export SWITCH_LIBS="-specs=$DEVKITPRO/libnx/switch.specs -L$DEVKITPRO/libnx/lib -lnx"
nim c ... --passC="-I$DEVKITPRO/libnx/include" ...
--passL="-specs=$DEVKITPRO/libnx/switch.specs -L$DEVKITPRO/libnx/lib -lnx"
or setup a nim.cfg file like so:
@@ -259,10 +259,6 @@ an nro file with the ``elf2nro`` tool in the DevkitPro release. Examples can be
`the nim-libnx github repo <https://github.com/jyapayne/nim-libnx.git>`_ or you can use
`the switch builder tool <https://github.com/jyapayne/switch-builder.git>`_.
Environment variables are:
- ``SWITCH_LIBS`` for any extra libraries required by your application (``-lLIBNAME`` or ``-LLIBPATH``)
- ``SWITCH_INCLUDES`` for any extra include files (``-IINCLUDE_PATH``)
There are a few things that don't work because the DevkitPro libraries don't support them.
They are:

View File

@@ -891,7 +891,7 @@ important differences:
future version of the compiler.)
However, you can also use a ``closure`` iterator to get a different set of
restrictions. See `first class iterators <manual.html#first-class-iterators>`_
restrictions. See `first class iterators <manual.html#iterators-and-the-for-statement-first-class-iterators>`_
for details. Iterators can have the same name and parameters as a proc, since
essentially they have their own namespaces. Therefore it is common practice to
wrap iterators in procs of the same name which accumulate the result of the

View File

@@ -1284,7 +1284,9 @@ proc customPragmaNode(n: NimNode): NimNode =
let
typ = n.getTypeInst()
if typ.typeKind == ntyTypeDesc:
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()
if impl[0].kind == nnkPragmaExpr:
return impl[0][1]

View File

@@ -272,7 +272,7 @@ proc asyncSockHandleWrite(h: RootRef) =
AsyncSocket(h).deleg.mode = fmRead
when defined(ssl):
proc asyncSockDoHandshake(h: PObject) {.gcsafe.} =
proc asyncSockDoHandshake(h: RootRef) {.gcsafe.} =
if AsyncSocket(h).socket.isSSL and not
AsyncSocket(h).socket.gotHandshake:
if AsyncSocket(h).sslNeedAccept:

View File

@@ -122,6 +122,21 @@ var
B9600* {.importc, header: "<termios.h>".}: Speed
B19200* {.importc, header: "<termios.h>".}: Speed
B38400* {.importc, header: "<termios.h>".}: Speed
B57600* {.importc, header: "<termios.h>".}: Speed
B115200* {.importc, header: "<termios.h>".}: Speed
B230400* {.importc, header: "<termios.h>".}: Speed
B460800* {.importc, header: "<termios.h>".}: Speed
B500000* {.importc, header: "<termios.h>".}: Speed
B576000* {.importc, header: "<termios.h>".}: Speed
B921600* {.importc, header: "<termios.h>".}: Speed
B1000000* {.importc, header: "<termios.h>".}: Speed
B1152000* {.importc, header: "<termios.h>".}: Speed
B1500000* {.importc, header: "<termios.h>".}: Speed
B2000000* {.importc, header: "<termios.h>".}: Speed
B2500000* {.importc, header: "<termios.h>".}: Speed
B3000000* {.importc, header: "<termios.h>".}: Speed
B3500000* {.importc, header: "<termios.h>".}: Speed
B4000000* {.importc, header: "<termios.h>".}: Speed
EXTA* {.importc, header: "<termios.h>".}: Speed
EXTB* {.importc, header: "<termios.h>".}: Speed
CSIZE* {.importc, header: "<termios.h>".}: Cflag

View File

@@ -1890,7 +1890,7 @@ proc entityToUtf8*(entity: string): string =
proc addNode(father, son: XmlNode) =
if son != nil: add(father, son)
proc parse(x: var XmlParser, errors: var seq[string]): XmlNode
proc parse(x: var XmlParser, errors: var seq[string]): XmlNode {.gcsafe.}
proc expected(x: var XmlParser, n: XmlNode): string =
result = errorMsg(x, "</" & n.tag & "> expected")

View File

@@ -66,7 +66,7 @@ proc raiseOSError*(errorCode: OSErrorCode; additionalInfo = "") {.noinline.} =
raise e
{.push stackTrace:off.}
proc osLastError*(): OSErrorCode =
proc osLastError*(): OSErrorCode {.sideEffect.} =
## Retrieves the last operating system error code.
##
## This procedure is useful in the event when an OS call fails. In that case

View File

@@ -322,8 +322,7 @@ type MemSlice* = object ## represent slice of a MemFile for iteration over deli
proc `==`*(x, y: MemSlice): bool =
## Compare a pair of MemSlice for strict equality.
proc memcmp(a, b: pointer, n:int):int {.importc: "memcmp",header: "string.h".}
result = (x.size == y.size and memcmp(x.data, y.data, x.size) == 0)
result = (x.size == y.size and equalMem(x.data, y.data, x.size))
proc `$`*(ms: MemSlice): string {.inline.} =
## Return a Nim string built from a MemSlice.

View File

@@ -189,7 +189,7 @@ proc `$`*[T](self: Option[T]): string =
if self.isSome:
"Some(" & $self.val & ")"
else:
"None[" & T.name & "]"
"None[" & name(T) & "]"
when isMainModule:
import unittest, sequtils
@@ -298,3 +298,17 @@ when isMainModule:
test "none[T]":
check(none[int]().isNone)
check(none(int) == none[int]())
test "$ on typed with .name":
type Named = object
name: string
let nobody = none(Named)
check($nobody == "None[Named]")
test "$ on type with name()":
type Person = object
myname: string
let noperson = none(Person)
check($noperson == "None[Person]")

View File

@@ -296,6 +296,26 @@ proc setCurrentDir*(newDir: string) {.inline, tags: [].} =
else:
if chdir(newDir) != 0'i32: raiseOSError(osLastError())
proc absolutePath*(path: string, root = getCurrentDir()): string =
## Returns the absolute path of `path`, rooted at `root` (which must be absolute)
## if `path` is absolute, return it, ignoring `root`
runnableExamples:
doAssert absolutePath("a") == getCurrentDir() / "a"
if isAbsolute(path): path
else:
if not root.isAbsolute:
raise newException(ValueError, "The specified root is not absolute: " & root)
joinPath(root, path)
when isMainModule:
doAssertRaises(ValueError): discard absolutePath("a", "b")
doAssert absolutePath("a") == getCurrentDir() / "a"
doAssert absolutePath("a", "/b") == "/b" / "a"
when defined(Posix):
doAssert absolutePath("a", "/b/") == "/b" / "a"
doAssert absolutePath("a", "/b/c") == "/b/c" / "a"
doAssert absolutePath("/a", "b/") == "/a"
proc expandFilename*(filename: string): string {.rtl, extern: "nos$1",
tags: [ReadDirEffect].} =
## Returns the full (`absolute`:idx:) path of an existing file `filename`,

View File

@@ -423,12 +423,22 @@ proc isAbsolute*(path: string): bool {.rtl, noSideEffect, extern: "nos$1".} =
## Checks whether a given `path` is absolute.
##
## On Windows, network paths are considered absolute too.
runnableExamples:
doAssert(not "".isAbsolute)
doAssert(not ".".isAbsolute)
when defined(posix):
doAssert "/".isAbsolute
doAssert(not "a/".isAbsolute)
if len(path) == 0: return false
when doslikeFileSystem:
var len = len(path)
result = (len > 0 and path[0] in {'/', '\\'}) or
result = (path[0] in {'/', '\\'}) or
(len > 1 and path[0] in {'a'..'z', 'A'..'Z'} and path[1] == ':')
elif defined(macos):
result = path.len > 0 and path[0] != ':'
# according to https://perldoc.perl.org/File/Spec/Mac.html `:a` is a relative path
result = path[0] != ':'
elif defined(RISCOS):
result = path[0] == '$'
elif defined(posix):

View File

@@ -683,8 +683,8 @@ when isMainModule:
# works:
import times
var nullTime: DateTime
check &"{nullTime:yyyy-mm-dd}", "0000-00-00"
var dt = initDateTime(01, mJan, 2000, 00, 00, 00)
check &"{dt:yyyy-MM-dd}", "2000-01-01"
var tm = fromUnix(0)
discard &"{tm}"

View File

@@ -19,22 +19,30 @@
import macros
import strformat
from strutils import toLowerAscii
import colors
import colors, tables
const
hasThreadSupport = compileOption("threads")
when defined(windows):
import winlean
when not hasThreadSupport:
import tables
var
colorsFGCache = initTable[Color, string]()
colorsBGCache = initTable[Color, string]()
styleCache = initTable[int, string]()
type
PTerminal = ref object
trueColorIsSupported: bool
trueColorIsEnabled: bool
fgSetColor: bool
when defined(windows):
hStdout: Handle
hStderr: Handle
oldStdoutAttr: int16
oldStderrAttr: int16
var
trueColorIsSupported: bool
trueColorIsEnabled: bool
fgSetColor: bool
var gTerm {.threadvar.}: PTerminal
proc newTerminal(): PTerminal
proc getTerminal(): PTerminal {.inline.} =
if isNil(gTerm):
gTerm = newTerminal()
result = gTerm
const
fgPrefix = "\x1b[38;2;"
@@ -156,23 +164,6 @@ when defined(windows):
proc setConsoleMode(hConsoleHandle: Handle, dwMode: DWORD): WINBOOL{.
stdcall, dynlib: "kernel32", importc: "SetConsoleMode".}
var
hStdout: Handle # = createFile("CONOUT$", GENERIC_WRITE, 0, nil,
# OPEN_ALWAYS, 0, 0)
hStderr: Handle
block:
var hStdoutTemp = getStdHandle(STD_OUTPUT_HANDLE)
if duplicateHandle(getCurrentProcess(), hStdoutTemp, getCurrentProcess(),
addr(hStdout), 0, 1, DUPLICATE_SAME_ACCESS) == 0:
when defined(consoleapp):
raiseOSError(osLastError())
var hStderrTemp = getStdHandle(STD_ERROR_HANDLE)
if duplicateHandle(getCurrentProcess(), hStderrTemp, getCurrentProcess(),
addr(hStderr), 0, 1, DUPLICATE_SAME_ACCESS) == 0:
when defined(consoleapp):
raiseOSError(osLastError())
proc getCursorPos(h: Handle): tuple [x,y: int] =
var c: CONSOLESCREENBUFFERINFO
if getConsoleScreenBufferInfo(h, addr(c)) == 0:
@@ -193,12 +184,23 @@ when defined(windows):
return c.wAttributes
return 0x70'i16 # ERROR: return white background, black text
var
oldStdoutAttr = getAttributes(hStdout)
oldStderrAttr = getAttributes(hStderr)
proc initTerminal(term: PTerminal) =
var hStdoutTemp = getStdHandle(STD_OUTPUT_HANDLE)
if duplicateHandle(getCurrentProcess(), hStdoutTemp, getCurrentProcess(),
addr(term.hStdout), 0, 1, DUPLICATE_SAME_ACCESS) == 0:
when defined(consoleapp):
raiseOSError(osLastError())
var hStderrTemp = getStdHandle(STD_ERROR_HANDLE)
if duplicateHandle(getCurrentProcess(), hStderrTemp, getCurrentProcess(),
addr(term.hStderr), 0, 1, DUPLICATE_SAME_ACCESS) == 0:
when defined(consoleapp):
raiseOSError(osLastError())
term.oldStdoutAttr = getAttributes(term.hStdout)
term.oldStderrAttr = getAttributes(term.hStderr)
template conHandle(f: File): Handle =
if f == stderr: hStderr else: hStdout
let term = getTerminal()
if f == stderr: term.hStderr else: term.hStdout
else:
import termios, posix, os, parseutils
@@ -306,7 +308,7 @@ proc setCursorPos*(f: File, x, y: int) =
let h = conHandle(f)
setCursorPos(h, x, y)
else:
f.write(fmt"{stylePrefix}{y};{x}f")
f.write(fmt"{stylePrefix}{y+1};{x+1}f")
proc setCursorXPos*(f: File, x: int) =
## Sets the terminal's cursor to the x position.
@@ -321,7 +323,7 @@ proc setCursorXPos*(f: File, x: int) =
if setConsoleCursorPosition(h, origin) == 0:
raiseOSError(osLastError())
else:
f.write(fmt"{stylePrefix}{x}G")
f.write(fmt"{stylePrefix}{x+1}G")
when defined(windows):
proc setCursorYPos*(f: File, y: int) =
@@ -459,10 +461,11 @@ proc eraseScreen*(f: File) =
proc resetAttributes*(f: File) =
## Resets all attributes.
when defined(windows):
let term = getTerminal()
if f == stderr:
discard setConsoleTextAttribute(hStderr, oldStderrAttr)
discard setConsoleTextAttribute(term.hStderr, term.oldStderrAttr)
else:
discard setConsoleTextAttribute(hStdout, oldStdoutAttr)
discard setConsoleTextAttribute(term.hStdout, term.oldStdoutAttr)
else:
f.write(ansiResetCode)
@@ -471,11 +474,12 @@ type
styleBright = 1, ## bright text
styleDim, ## dim text
styleItalic, ## italic (or reverse on terminals not supporting)
styleUnderscore = 4, ## underscored text
styleUnderscore, ## underscored text
styleBlink, ## blinking/bold text
styleReverse = 7, ## reverse
styleHidden ## hidden text
styleStrikethrough, ## strikethrough
styleBlinkRapid, ## rapid blinking/bold text (not widely supported)
styleReverse, ## reverse
styleHidden, ## hidden text
styleStrikethrough ## strikethrough
{.deprecated: [TStyle: Style].}
{.deprecated: [styleUnknown: styleItalic].}
@@ -486,14 +490,7 @@ when not defined(windows):
gBG {.threadvar.}: int
proc ansiStyleCode*(style: int): string =
when hasThreadSupport:
result = fmt"{stylePrefix}{style}m"
else:
if styleCache.hasKey(style):
result = styleCache[style]
else:
result = fmt"{stylePrefix}{style}m"
styleCache[style] = result
result = fmt"{stylePrefix}{style}m"
template ansiStyleCode*(style: Style): string =
ansiStyleCode(style.int)
@@ -520,10 +517,11 @@ proc setStyle*(f: File, style: set[Style]) =
proc writeStyled*(txt: string, style: set[Style] = {styleBright}) =
## Writes the text `txt` in a given `style` to stdout.
when defined(windows):
var old = getAttributes(hStdout)
let term = getTerminal()
var old = getAttributes(term.hStdout)
stdout.setStyle(style)
stdout.write(txt)
discard setConsoleTextAttribute(hStdout, old)
discard setConsoleTextAttribute(term.hStdout, old)
else:
stdout.setStyle(style)
stdout.write(txt)
@@ -632,32 +630,16 @@ template ansiForegroundColorCode*(fg: static[ForegroundColor],
ansiStyleCode(fg.int + bright.int * 60)
proc ansiForegroundColorCode*(color: Color): string =
when hasThreadSupport:
let rgb = extractRGB(color)
result = fmt"{fgPrefix}{rgb.r};{rgb.g};{rgb.b}m"
else:
if colorsFGCache.hasKey(color):
result = colorsFGCache[color]
else:
let rgb = extractRGB(color)
result = fmt"{fgPrefix}{rgb.r};{rgb.g};{rgb.b}m"
colorsFGCache[color] = result
let rgb = extractRGB(color)
result = fmt"{fgPrefix}{rgb.r};{rgb.g};{rgb.b}m"
template ansiForegroundColorCode*(color: static[Color]): string =
const rgb = extractRGB(color)
(static(fmt"{fgPrefix}{rgb.r};{rgb.g};{rgb.b}m"))
proc ansiBackgroundColorCode*(color: Color): string =
when hasThreadSupport:
let rgb = extractRGB(color)
result = fmt"{bgPrefix}{rgb.r};{rgb.g};{rgb.b}m"
else:
if colorsBGCache.hasKey(color):
result = colorsBGCache[color]
else:
let rgb = extractRGB(color)
result = fmt"{bgPrefix}{rgb.r};{rgb.g};{rgb.b}m"
colorsFGCache[color] = result
let rgb = extractRGB(color)
result = fmt"{bgPrefix}{rgb.r};{rgb.g};{rgb.b}m"
template ansiBackgroundColorCode*(color: static[Color]): string =
const rgb = extractRGB(color)
@@ -665,16 +647,17 @@ template ansiBackgroundColorCode*(color: static[Color]): string =
proc setForegroundColor*(f: File, color: Color) =
## Sets the terminal's foreground true color.
if trueColorIsEnabled:
if getTerminal().trueColorIsEnabled:
f.write(ansiForegroundColorCode(color))
proc setBackgroundColor*(f: File, color: Color) =
## Sets the terminal's background true color.
if trueColorIsEnabled:
if getTerminal().trueColorIsEnabled:
f.write(ansiBackgroundColorCode(color))
proc setTrueColor(f: File, color: Color) =
if fgSetColor:
let term = getTerminal()
if term.fgSetColor:
setForegroundColor(f, color)
else:
setBackgroundColor(f, color)
@@ -726,11 +709,10 @@ macro styledWrite*(f: File, m: varargs[typed]): untyped =
## stdout.styledWrite(fgRed, "red text ")
## stdout.styledWrite(fgGreen, "green text")
##
let m = callsite()
var reset = false
result = newNimNode(nnkStmtList)
for i in countup(2, m.len - 1):
for i in countup(0, m.len - 1):
let item = m[i]
case item.kind
of nnkStrLit..nnkTripleStrLit:
@@ -871,56 +853,65 @@ proc resetAttributes*() {.noconv.} =
proc isTrueColorSupported*(): bool =
## Returns true if a terminal supports true color.
return trueColorIsSupported
return getTerminal().trueColorIsSupported
when defined(windows):
import os
proc enableTrueColors*() =
## Enable true color.
var term = getTerminal()
when defined(windows):
var
ver: OSVERSIONINFO
ver.dwOSVersionInfoSize = sizeof(ver).DWORD
let res = getVersionExW(addr ver)
if res == 0:
trueColorIsSupported = false
term.trueColorIsSupported = false
else:
trueColorIsSupported = ver.dwMajorVersion > 10 or
term.trueColorIsSupported = ver.dwMajorVersion > 10 or
(ver.dwMajorVersion == 10 and (ver.dwMinorVersion > 0 or
(ver.dwMinorVersion == 0 and ver.dwBuildNumber >= 10586)))
if not trueColorIsSupported:
trueColorIsSupported = getEnv("ANSICON_DEF").len > 0
if not term.trueColorIsSupported:
term.trueColorIsSupported = getEnv("ANSICON_DEF").len > 0
if trueColorIsSupported:
if term.trueColorIsSupported:
if getEnv("ANSICON_DEF").len == 0:
var mode: DWORD = 0
if getConsoleMode(getStdHandle(STD_OUTPUT_HANDLE), addr(mode)) != 0:
mode = mode or ENABLE_VIRTUAL_TERMINAL_PROCESSING
if setConsoleMode(getStdHandle(STD_OUTPUT_HANDLE), mode) != 0:
trueColorIsEnabled = true
term.trueColorIsEnabled = true
else:
trueColorIsEnabled = false
term.trueColorIsEnabled = false
else:
trueColorIsEnabled = true
term.trueColorIsEnabled = true
else:
trueColorIsSupported = string(getEnv("COLORTERM")).toLowerAscii() in ["truecolor", "24bit"]
trueColorIsEnabled = trueColorIsSupported
term.trueColorIsSupported = string(getEnv("COLORTERM")).toLowerAscii() in ["truecolor", "24bit"]
term.trueColorIsEnabled = term.trueColorIsSupported
proc disableTrueColors*() =
## Disable true color.
var term = getTerminal()
when defined(windows):
if trueColorIsSupported:
if term.trueColorIsSupported:
if getEnv("ANSICON_DEF").len == 0:
var mode: DWORD = 0
if getConsoleMode(getStdHandle(STD_OUTPUT_HANDLE), addr(mode)) != 0:
mode = mode and not ENABLE_VIRTUAL_TERMINAL_PROCESSING
discard setConsoleMode(getStdHandle(STD_OUTPUT_HANDLE), mode)
trueColorIsEnabled = false
term.trueColorIsEnabled = false
else:
trueColorIsEnabled = false
term.trueColorIsEnabled = false
proc newTerminal(): PTerminal =
new result
when defined(windows):
initTerminal(result)
when not defined(testing) and isMainModule:
assert ansiStyleCode(styleBright) == "\e[1m"
assert ansiStyleCode(styleStrikethrough) == "\e[9m"
#system.addQuitProc(resetAttributes)
write(stdout, "never mind")
stdout.eraseLine()

File diff suppressed because it is too large Load Diff

View File

@@ -275,14 +275,14 @@ proc high*[T: Ordinal](x: T): T {.magic: "High", noSideEffect.}
## high(2) #=> 9223372036854775807
## high(int) #=> 9223372036854775807
proc high*[T: Ordinal](x: typeDesc[T]): T {.magic: "High", noSideEffect.}
proc high*[T: Ordinal|enum](x: typeDesc[T]): T {.magic: "High", noSideEffect.}
proc high*[T](x: openArray[T]): int {.magic: "High", noSideEffect.}
proc high*[I, T](x: array[I, T]): I {.magic: "High", noSideEffect.}
proc high*[I, T](x: typeDesc[array[I, T]]): I {.magic: "High", noSideEffect.}
proc high*(x: cstring): int {.magic: "High", noSideEffect.}
proc high*(x: string): int {.magic: "High", noSideEffect.}
proc low*[T: Ordinal](x: typeDesc[T]): T {.magic: "Low", noSideEffect.}
proc low*[T: Ordinal|enum](x: typeDesc[T]): T {.magic: "Low", noSideEffect.}
proc low*[T](x: openArray[T]): int {.magic: "Low", noSideEffect.}
proc low*[I, T](x: array[I, T]): I {.magic: "Low", noSideEffect.}
proc low*[T](x: T): T {.magic: "Low", noSideEffect.}
@@ -806,7 +806,7 @@ proc card*[T](x: set[T]): int {.magic: "Card", noSideEffect.}
## var i = {1,2,3,4}
## card(i) #=> 4
proc ord*[T](x: T): int {.magic: "Ord", noSideEffect.}
proc ord*[T: Ordinal|enum](x: T): int {.magic: "Ord", noSideEffect.}
## returns the internal int value of an ordinal value ``x``.
##
## .. code-block:: nim
@@ -2959,6 +2959,22 @@ when not defined(JS): #and not defined(nimscript):
## useful for low-level file access
include "system/ansi_c"
include "system/memory"
proc zeroMem(p: pointer, size: Natural) =
nimZeroMem(p, size)
when declared(memTrackerOp):
memTrackerOp("zeroMem", p, size)
proc copyMem(dest, source: pointer, size: Natural) =
nimCopyMem(dest, source, size)
when declared(memTrackerOp):
memTrackerOp("copyMem", dest, size)
proc moveMem(dest, source: pointer, size: Natural) =
c_memmove(dest, source, size)
when declared(memTrackerOp):
memTrackerOp("moveMem", dest, size)
proc equalMem(a, b: pointer, size: Natural): bool =
nimCmpMem(a, b, size) == 0
proc cmp(x, y: string): int =
when nimvm:
@@ -2967,7 +2983,7 @@ when not defined(JS): #and not defined(nimscript):
else: result = 0
else:
let minlen = min(x.len, y.len)
result = int(c_memcmp(x.cstring, y.cstring, minlen.csize))
result = int(nimCmpMem(x.cstring, y.cstring, minlen.csize))
if result == 0:
result = x.len - y.len
@@ -3256,22 +3272,6 @@ when not defined(JS): #and not defined(nimscript):
when defined(memtracker):
include "system/memtracker"
when not defined(nimscript):
proc zeroMem(p: pointer, size: Natural) =
c_memset(p, 0, size)
when declared(memTrackerOp):
memTrackerOp("zeroMem", p, size)
proc copyMem(dest, source: pointer, size: Natural) =
c_memcpy(dest, source, size)
when declared(memTrackerOp):
memTrackerOp("copyMem", dest, size)
proc moveMem(dest, source: pointer, size: Natural) =
c_memmove(dest, source, size)
when declared(memTrackerOp):
memTrackerOp("moveMem", dest, size)
proc equalMem(a, b: pointer, size: Natural): bool =
c_memcmp(a, b, size) == 0
when hostOS == "standalone":
include "system/embedded"
else:
@@ -4154,11 +4154,10 @@ template doAssertRaises*(exception, code: untyped): typed =
## .. code-block:: nim
## doAssertRaises(ValueError):
## raise newException(ValueError, "Hello World")
# TODO: investigate why runnableExamples here caused
# https://github.com/nim-lang/Nim/issues/8223
var wrong = false
try:
code
if true:
code
wrong = true
except exception:
discard

View File

@@ -830,7 +830,7 @@ proc rawDealloc(a: var MemRegion, p: pointer) =
c.freeList = f
when overwriteFree:
# set to 0xff to check for usage after free bugs:
c_memset(cast[pointer](cast[int](p) +% sizeof(FreeCell)), -1'i32,
nimSetMem(cast[pointer](cast[int](p) +% sizeof(FreeCell)), -1'i32,
s -% sizeof(FreeCell))
# check if it is not in the freeSmallChunks[s] list:
if c.free < s:
@@ -847,7 +847,7 @@ proc rawDealloc(a: var MemRegion, p: pointer) =
s == 0, "rawDealloc 2")
else:
# set to 0xff to check for usage after free bugs:
when overwriteFree: c_memset(p, -1'i32, c.size -% bigChunkOverhead())
when overwriteFree: nimSetMem(p, -1'i32, c.size -% bigChunkOverhead())
# free big chunk
var c = cast[PBigChunk](c)
dec a.occ, c.size

View File

@@ -25,6 +25,8 @@ proc c_memset(p: pointer, value: cint, size: csize): pointer {.
importc: "memset", header: "<string.h>", discardable.}
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.}
when defined(linux) and defined(amd64):
type

47
lib/system/memory.nim Normal file
View File

@@ -0,0 +1,47 @@
const useLibC = not defined(nimNoLibc)
proc nimCopyMem(dest, source: pointer, size: Natural) {.compilerproc, inline.} =
when useLibC:
c_memcpy(dest, source, size)
else:
let d = cast[ptr UncheckedArray[byte]](dest)
let s = cast[ptr UncheckedArray[byte]](source)
var i = 0
while i < size:
d[i] = s[i]
inc i
proc nimSetMem(a: pointer, v: cint, size: Natural) {.inline.} =
when useLibC:
c_memset(a, v, size)
else:
let a = cast[ptr UncheckedArray[byte]](a)
var i = 0
let v = cast[byte](v)
while i < size:
a[i] = v
inc i
proc nimZeroMem(p: pointer, size: Natural) {.compilerproc, inline.} =
nimSetMem(p, 0, size)
proc nimCmpMem(a, b: pointer, size: Natural): cint {.compilerproc, inline.} =
when useLibC:
c_memcmp(a, b, size)
else:
let a = cast[ptr UncheckedArray[byte]](a)
let b = cast[ptr UncheckedArray[byte]](b)
var i = 0
while i < size:
let d = a[i].cint - b[i].cint
if d != 0: return d
inc i
proc nimCStrLen(a: cstring): csize {.compilerproc, inline.} =
when useLibC:
c_strlen(a)
else:
var a = cast[ptr byte](a)
while a[] != 0:
a = cast[ptr byte](cast[uint](a) + 1)
inc result

View File

@@ -154,7 +154,7 @@ proc readLine(f: File, line: var TaintedString): bool =
while true:
# memset to \L so that we can tell how far fgets wrote, even on EOF, where
# fgets doesn't append an \L
c_memset(addr line.string[pos], '\L'.ord, sp)
nimSetMem(addr line.string[pos], '\L'.ord, sp)
var fgetsSuccess = c_fgets(addr line.string[pos], sp, f) != nil
if not fgetsSuccess: checkErr(f)
let m = c_memchr(addr line.string[pos], '\L'.ord, sp)

View File

@@ -327,6 +327,7 @@ proc ERR_load_BIO_strings*(){.cdecl, dynlib: DLLUtilName, importc.}
proc SSL_new*(context: SslCtx): SslPtr{.cdecl, dynlib: DLLSSLName, importc.}
proc SSL_free*(ssl: SslPtr){.cdecl, dynlib: DLLSSLName, importc.}
proc SSL_get_SSL_CTX*(ssl: SslPtr): SslCtx {.cdecl, dynlib: DLLSSLName, importc.}
proc SSL_set_SSL_CTX*(ssl: SslPtr, ctx: SslCtx): SslCtx {.cdecl, dynlib: DLLSSLName, importc.}
proc SSL_CTX_new*(meth: PSSL_METHOD): SslCtx{.cdecl,
dynlib: DLLSSLName, importc.}
proc SSL_CTX_load_verify_locations*(ctx: SslCtx, CAfile: cstring,

10
tests/casestmt/t8333.nim Normal file
View File

@@ -0,0 +1,10 @@
discard """
output: "1"
"""
converter toInt*(x: char): int =
x.int
case 0
of 'a': echo 0
else: echo 1

16
tests/concepts/t8280.nim Normal file
View File

@@ -0,0 +1,16 @@
discard """
output: "()"
"""
type
Iterable[T] = concept x
for elem in x:
elem is T
proc max[A](iter: Iterable[A]): A =
discard
type
MyType = object
echo max(@[MyType()])

31
tests/converter/t7098.nim Normal file
View File

@@ -0,0 +1,31 @@
type
Byte* = uint8
Bytes* = seq[Byte]
BytesRange* = object
bytes: Bytes
ibegin, iend: int
proc initBytesRange*(s: var Bytes, ibegin = 0, iend = -1): BytesRange =
let e = if iend < 0: s.len + iend + 1
else: iend
assert ibegin >= 0 and e <= s.len
shallow(s)
result.bytes = s
result.ibegin = ibegin
result.iend = e
converter fromSeq*(s: Bytes): BytesRange =
var seqCopy = s
return initBytesRange(seqCopy)
type
Reader* = object
data: BytesRange
position: int
proc readerFromBytes*(input: BytesRange): Reader =
discard
let r = readerFromBytes(@[])

15
tests/generics/t7794.nim Normal file
View File

@@ -0,0 +1,15 @@
discard """
output: '''
10
2.0
'''
"""
type
Data*[T:SomeNumber, U:SomeReal] = ref object
x*: T
value*: U
var d = Data[int, float64](x:10.int, value:2'f64)
echo d.x
echo d.value

7
tests/generics/t8270.nim Normal file
View File

@@ -0,0 +1,7 @@
discard """
line: 6
errormsg: "cannot instantiate: \'T\'"
"""
proc m[T](x: T): int = discard
echo [m]

21
tests/init/t8314.nim Normal file
View File

@@ -0,0 +1,21 @@
discard """
nimout: '''
t8314.nim(8, 7) Hint: BEGIN [User]
t8314.nim(19, 7) Hint: END [User]
'''
"""
{.hint: "BEGIN".}
proc foo(x: range[1..10]) =
block:
var (y,) = (x,)
echo y
block:
var (_,y) = (1,x)
echo y
block:
var (y,_,) = (x,1,)
echo y
{.hint: "END".}
foo(1)

View File

@@ -36,8 +36,8 @@ let utcPlus2 = Timezone(zoneInfoFromUtc: staticZoneInfoFromUtc, zoneInfoFromTz:
block timezoneTests:
let dt = initDateTime(01, mJan, 2017, 12, 00, 00, utcPlus2)
doAssert $dt == "2017-01-01T12:00:00+02:00"
doAssert $dt.utc == "2017-01-01T10:00:00+00:00"
doAssert $dt.utc == "2017-01-01T10:00:00Z"
doAssert $dt.utc.inZone(utcPlus2) == $dt
doAssert $initDateTime(01, mJan, 1911, 12, 00, 00, utc()) == "1911-01-01T12:00:00+00:00"
doAssert $initDateTime(01, mJan, 0023, 12, 00, 00, utc()) == "0023-01-01T12:00:00+00:00"
doAssert $initDateTime(01, mJan, 1911, 12, 00, 00, utc()) == "1911-01-01T12:00:00Z"
doAssert $initDateTime(01, mJan, 0023, 12, 00, 00, utc()) == "0023-01-01T12:00:00Z"

View File

@@ -24,3 +24,23 @@ macro foo: typed =
else: echo "Does not compute! (test OK)"
foo()
#------------------------------------
# bug #8287
type MyString = distinct string
proc `$` (c: MyString): string {.borrow.}
proc `!!` (c: cstring): int =
c.len
proc f(name: MyString): int =
!! $ name
macro repr_and_parse(fn: typed): typed =
let fn_impl = fn.getImpl
fn_impl.name = genSym(nskProc, $fn_impl.name)
echo fn_impl.repr
result = parseStmt(fn_impl.repr)
repr_and_parse(f)

View File

@@ -9,7 +9,7 @@ proc printfImpl(formatstr: cstring) {.importc: "printf", varargs.}
iterator tokenize(format: string): char =
var i = 0
while true:
while i < format.len:
case format[i]
of '%':
case format[i+1]
@@ -42,7 +42,8 @@ macro printf(formatString: string{lit}, args: varargs[typed]): untyped =
$expectedType & ", actual type: " & $actualType
# keep the original callsite, but use cprintf instead
result = callsite()
result[0] = bindSym"printfImpl"
result = newCall(bindSym"printfImpl")
result.add formatString
for a in args: result.add a
printf("test %d\n", 10)

View File

@@ -5,4 +5,7 @@ discard """
# Tests that module names can contain multi byte characters
let a = 1
doAssert åäö.a == 1
doAssert åäö.a == 1
proc inlined() {.inline.} = discard
inlined()

View File

@@ -145,4 +145,12 @@ block:
type Annotated {.simpleAttr.} = object
proc generic_proc[T]() =
assert Annotated.hasCustomPragma(simpleAttr)
assert Annotated.hasCustomPragma(simpleAttr)
#--------------------------------------------------------------------------
# Pragma on proc type
let a: proc(x: int) {.defaultValue(5).} = nil
static:
doAssert hasCustomPragma(a.type, defaultValue)

View File

@@ -8,6 +8,24 @@ discard """
import
times, os, strutils, unittest
proc staticTz(hours, minutes, seconds: int = 0): Timezone {.noSideEffect.} =
let offset = hours * 3600 + minutes * 60 + seconds
proc zoneInfoFromTz(adjTime: Time): ZonedTime {.locks: 0.} =
result.isDst = false
result.utcOffset = offset
result.adjTime = adjTime
proc zoneInfoFromUtc(time: Time): ZonedTime {.locks: 0.}=
result.isDst = false
result.utcOffset = offset
result.adjTime = fromUnix(time.toUnix - offset)
result.name = ""
result.zoneInfoFromTz = zoneInfoFromTz
result.zoneInfoFromUtc = zoneInfoFromUtc
# $ date --date='@2147483647'
# Tue 19 Jan 03:14:07 GMT 2038
@@ -19,25 +37,10 @@ proc checkFormat(t: DateTime, format, expected: string) =
echo "actual : ", actual
doAssert false
let t = fromUnix(2147483647).utc
t.checkFormat("ddd dd MMM hh:mm:ss yyyy", "Tue 19 Jan 03:14:07 2038")
t.checkFormat("ddd ddMMMhh:mm:ssyyyy", "Tue 19Jan03:14:072038")
t.checkFormat("d dd ddd dddd h hh H HH m mm M MM MMM MMMM s" &
" ss t tt y yy yyy yyyy yyyyy z zz zzz",
"19 19 Tue Tuesday 3 03 3 03 14 14 1 01 Jan January 7 07 A AM 8 38 038 2038 02038 +0 +00 +00:00")
t.checkFormat("yyyyMMddhhmmss", "20380119031407")
# issue 7620
let t7620_am = parse("4/15/2017 12:01:02 AM +0", "M/d/yyyy' 'h:mm:ss' 'tt' 'z", utc())
t7620_am.checkFormat("M/d/yyyy' 'h:mm:ss' 'tt' 'z", "4/15/2017 12:01:02 AM +0")
let t7620_pm = parse("4/15/2017 12:01:02 PM +0", "M/d/yyyy' 'h:mm:ss' 'tt' 'z", utc())
t7620_pm.checkFormat("M/d/yyyy' 'h:mm:ss' 'tt' 'z", "4/15/2017 12:01:02 PM +0")
let t2 = fromUnix(160070789).utc # Mon 27 Jan 16:06:29 GMT 1975
let t2 = fromUnix(160070789).utc() # Mon 27 Jan 16:06:29 GMT 1975
t2.checkFormat("d dd ddd dddd h hh H HH m mm M MM MMM MMMM s" &
" ss t tt y yy yyy yyyy yyyyy z zz zzz",
"27 27 Mon Monday 4 04 16 16 6 06 1 01 Jan January 29 29 P PM 5 75 975 1975 01975 +0 +00 +00:00")
"27 27 Mon Monday 4 04 16 16 6 06 1 01 Jan January 29 29 P PM 5 75 975 1975 01975 Z Z Z")
var t4 = fromUnix(876124714).utc # Mon 6 Oct 08:58:34 BST 1997
t4.checkFormat("M MM MMM MMMM", "10 10 Oct October")
@@ -83,16 +86,16 @@ let seqB: seq[Time] = @[]
doAssert seqA == seqB
for tz in [
(0, "+0", "+00", "+00:00"), # UTC
(-3600, "+1", "+01", "+01:00"), # CET
(-39600, "+11", "+11", "+11:00"), # two digits
(-1800, "+0", "+00", "+00:30"), # half an hour
(7200, "-2", "-02", "-02:00"), # positive
(38700, "-10", "-10", "-10:45")]: # positive with three quaters hour
let ti = DateTime(month: mJan, monthday: 1, utcOffset: tz[0])
doAssert ti.format("z") == tz[1]
doAssert ti.format("zz") == tz[2]
doAssert ti.format("zzz") == tz[3]
(staticTz(seconds = 0), "+0", "+00", "+00:00"), # UTC
(staticTz(seconds = -3600), "+1", "+01", "+01:00"), # CET
(staticTz(seconds = -39600), "+11", "+11", "+11:00"), # two digits
(staticTz(seconds = -1800), "+0", "+00", "+00:30"), # half an hour
(staticTz(seconds = 7200), "-2", "-02", "-02:00"), # positive
(staticTz(seconds = 38700), "-10", "-10", "-10:45")]: # positive with three quaters hour
let dt = initDateTime(1, mJan, 2000, 00, 00, 00, tz[0])
doAssert dt.format("z") == tz[1]
doAssert dt.format("zz") == tz[2]
doAssert dt.format("zzz") == tz[3]
block countLeapYears:
# 1920, 2004 and 2020 are leap years, and should be counted starting at the following year
@@ -112,11 +115,9 @@ template parseTest(s, f, sExpected: string, ydExpected: int) =
let
parsed = s.parse(f, utc())
parsedStr = $parsed
if parsedStr != sExpected:
echo "GOT ", parsedStr, " EXPECTED ", sExpected, " FORMAT ", f
check parsedStr == sExpected
if parsed.yearday != ydExpected:
echo s
echo parsed.repr
echo parsed.yearday, " exp: ", ydExpected
check(parsed.yearday == ydExpected)
template parseTestExcp(s, f: string) =
@@ -130,51 +131,43 @@ template parseTestTimeOnly(s, f, sExpected: string) =
# explicit timezone offsets in all tests.
template runTimezoneTests() =
parseTest("Tuesday at 09:04am on Dec 15, 2015 +0",
"dddd at hh:mmtt on MMM d, yyyy z", "2015-12-15T09:04:00+00:00", 348)
"dddd 'at' hh:mmtt 'on' MMM d, yyyy z", "2015-12-15T09:04:00Z", 348)
# ANSIC = "Mon Jan _2 15:04:05 2006"
parseTest("Thu Jan 12 15:04:05 2006 +0", "ddd MMM dd HH:mm:ss yyyy z",
"2006-01-12T15:04:05+00:00", 11)
"2006-01-12T15:04:05Z", 11)
# UnixDate = "Mon Jan _2 15:04:05 MST 2006"
parseTest("Thu Jan 12 15:04:05 2006 +0", "ddd MMM dd HH:mm:ss yyyy z",
"2006-01-12T15:04:05+00:00", 11)
"2006-01-12T15:04:05Z", 11)
# RubyDate = "Mon Jan 02 15:04:05 -0700 2006"
parseTest("Mon Feb 29 15:04:05 -07:00 2016 +0", "ddd MMM dd HH:mm:ss zzz yyyy z",
"2016-02-29T15:04:05+00:00", 59) # leap day
"2016-02-29T15:04:05Z", 59) # leap day
# RFC822 = "02 Jan 06 15:04 MST"
parseTest("12 Jan 16 15:04 +0", "dd MMM yy HH:mm z",
"2016-01-12T15:04:00+00:00", 11)
"2016-01-12T15:04:00Z", 11)
# RFC822Z = "02 Jan 06 15:04 -0700" # RFC822 with numeric zone
parseTest("01 Mar 16 15:04 -07:00", "dd MMM yy HH:mm zzz",
"2016-03-01T22:04:00+00:00", 60) # day after february in leap year
"2016-03-01T22:04:00Z", 60) # day after february in leap year
# RFC850 = "Monday, 02-Jan-06 15:04:05 MST"
parseTest("Monday, 12-Jan-06 15:04:05 +0", "dddd, dd-MMM-yy HH:mm:ss z",
"2006-01-12T15:04:05+00:00", 11)
"2006-01-12T15:04:05Z", 11)
# RFC1123 = "Mon, 02 Jan 2006 15:04:05 MST"
parseTest("Sun, 01 Mar 2015 15:04:05 +0", "ddd, dd MMM yyyy HH:mm:ss z",
"2015-03-01T15:04:05+00:00", 59) # day after february in non-leap year
"2015-03-01T15:04:05Z", 59) # day after february in non-leap year
# RFC1123Z = "Mon, 02 Jan 2006 15:04:05 -0700" # RFC1123 with numeric zone
parseTest("Thu, 12 Jan 2006 15:04:05 -07:00", "ddd, dd MMM yyyy HH:mm:ss zzz",
"2006-01-12T22:04:05+00:00", 11)
"2006-01-12T22:04:05Z", 11)
# RFC3339 = "2006-01-02T15:04:05Z07:00"
parseTest("2006-01-12T15:04:05Z-07:00", "yyyy-MM-ddTHH:mm:ssZzzz",
"2006-01-12T22:04:05+00:00", 11)
parseTest("2006-01-12T15:04:05Z-07:00", "yyyy-MM-dd'T'HH:mm:ss'Z'zzz",
"2006-01-12T22:04:05+00:00", 11)
"2006-01-12T22:04:05Z", 11)
# RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00"
parseTest("2006-01-12T15:04:05.999999999Z-07:00",
"yyyy-MM-ddTHH:mm:ss.999999999Zzzz", "2006-01-12T22:04:05+00:00", 11)
"yyyy-MM-dd'T'HH:mm:ss'.999999999Z'zzz", "2006-01-12T22:04:05Z", 11)
for tzFormat in ["z", "zz", "zzz"]:
# formatting timezone as 'Z' for UTC
parseTest("2001-01-12T22:04:05Z", "yyyy-MM-dd'T'HH:mm:ss" & tzFormat,
"2001-01-12T22:04:05+00:00", 11)
"2001-01-12T22:04:05Z", 11)
# Kitchen = "3:04PM"
parseTestTimeOnly("3:04PM", "h:mmtt", "15:04:00")
#when not defined(testing):
# echo "Kitchen: " & $s.parse(f)
# var ti = timeToTimeInfo(getTime())
# echo "Todays date after decoding: ", ti
# var tint = timeToTimeInterval(getTime())
# echo "Todays date after decoding to interval: ", tint
# Bug with parse not setting DST properly if the current local DST differs from
# the date being parsed. Need to test parse dates both in and out of DST. We
@@ -195,8 +188,8 @@ template runTimezoneTests() =
let
parsedJan = parse("2016-01-05 04:00:00+01:00", "yyyy-MM-dd HH:mm:sszzz")
parsedJul = parse("2016-07-01 04:00:00+01:00", "yyyy-MM-dd HH:mm:sszzz")
doAssert toTime(parsedJan).toUnix == 1451962800
doAssert toTime(parsedJul).toUnix == 1467342000
check toTime(parsedJan).toUnix == 1451962800
check toTime(parsedJul).toUnix == 1467342000
suite "ttimes":
@@ -256,7 +249,7 @@ suite "ttimes":
check $(dt + initDuration(days = 1)) == "2017-03-26T13:00:00+02:00"
test "datetime before epoch":
check $fromUnix(-2147483648).utc == "1901-12-13T20:45:52+00:00"
check $fromUnix(-2147483648).utc == "1901-12-13T20:45:52Z"
test "adding/subtracting time across dst":
putenv("TZ", "Europe/Stockholm")
@@ -319,6 +312,15 @@ suite "ttimes":
test "incorrect inputs: timezone (zzz) 3":
parseTestExcp("2018-02-19 16:30:00 +01:0", "yyyy-MM-dd hh:mm:ss zzz")
test "incorrect inputs: year (yyyy/uuuu)":
parseTestExcp("-0001", "yyyy")
parseTestExcp("-0001", "YYYY")
parseTestExcp("1", "yyyy")
parseTestExcp("12345", "yyyy")
parseTestExcp("1", "uuuu")
parseTestExcp("12345", "uuuu")
parseTestExcp("-1 BC", "UUUU g")
test "dynamic timezone":
proc staticOffset(offset: int): Timezone =
proc zoneInfoFromTz(adjTime: Time): ZonedTime =
@@ -340,7 +342,7 @@ suite "ttimes":
check dt.utcOffset == -9000
check dt.isDst == false
check $dt == "2000-01-01T12:00:00+02:30"
check $dt.utc == "2000-01-01T09:30:00+00:00"
check $dt.utc == "2000-01-01T09:30:00Z"
check $dt.utc.inZone(tz) == $dt
test "isLeapYear":
@@ -351,12 +353,12 @@ suite "ttimes":
test "subtract months":
var dt = initDateTime(1, mFeb, 2017, 00, 00, 00, utc())
check $(dt - initTimeInterval(months = 1)) == "2017-01-01T00:00:00+00:00"
check $(dt - initTimeInterval(months = 1)) == "2017-01-01T00:00:00Z"
dt = initDateTime(15, mMar, 2017, 00, 00, 00, utc())
check $(dt - initTimeInterval(months = 1)) == "2017-02-15T00:00:00+00:00"
check $(dt - initTimeInterval(months = 1)) == "2017-02-15T00:00:00Z"
dt = initDateTime(31, mMar, 2017, 00, 00, 00, utc())
# This happens due to monthday overflow. It's consistent with Phobos.
check $(dt - initTimeInterval(months = 1)) == "2017-03-03T00:00:00+00:00"
check $(dt - initTimeInterval(months = 1)) == "2017-03-03T00:00:00Z"
test "duration":
let d = initDuration
@@ -384,11 +386,11 @@ suite "ttimes":
discard initDateTime(1, mJan, -35_000, 12, 00, 00)
discard initDateTime(1, mJan, 35_000, 12, 00, 00)
# with duration/timeinterval
let dt = initDateTime(1, mJan, 35_000, 12, 00, 00, utc()) +
let dt = initDateTime(1, mJan, -35_000, 12, 00, 00, utc()) +
initDuration(seconds = 1)
check dt.second == 1
let dt2 = dt + 35_001.years
check $dt2 == "0001-01-01T12:00:01+00:00"
check $dt2 == "0001-01-01T12:00:01Z"
test "compare datetimes":
var dt1 = now()
@@ -426,4 +428,90 @@ suite "ttimes":
check (-1).fromWinTime.nanosecond == convert(Seconds, Nanoseconds, 1) - 100
check -1.fromWinTime.toWinTime == -1
# One nanosecond is discarded due to differences in time resolution
check initTime(0, 101).toWinTime.fromWinTime.nanosecond == 100
check initTime(0, 101).toWinTime.fromWinTime.nanosecond == 100
check initTime(0, 101).toWinTime.fromWinTime.nanosecond == 100
test "issue 7620":
let layout = "M/d/yyyy' 'h:mm:ss' 'tt' 'z"
let t7620_am = parse("4/15/2017 12:01:02 AM +0", layout, utc())
check t7620_am.format(layout) == "4/15/2017 12:01:02 AM Z"
let t7620_pm = parse("4/15/2017 12:01:02 PM +0", layout, utc())
check t7620_pm.format(layout) == "4/15/2017 12:01:02 PM Z"
test "format":
var dt = initDateTime(1, mJan, -0001,
17, 01, 02, 123_456_789,
staticTz(hours = 1, minutes = 2, seconds = 3))
check dt.format("d") == "1"
check dt.format("dd") == "01"
check dt.format("ddd") == "Fri"
check dt.format("dddd") == "Friday"
check dt.format("h") == "5"
check dt.format("hh") == "05"
check dt.format("H") == "17"
check dt.format("HH") == "17"
check dt.format("m") == "1"
check dt.format("mm") == "01"
check dt.format("M") == "1"
check dt.format("MM") == "01"
check dt.format("MMM") == "Jan"
check dt.format("MMMM") == "January"
check dt.format("s") == "2"
check dt.format("ss") == "02"
check dt.format("t") == "P"
check dt.format("tt") == "PM"
check dt.format("yy") == "02"
check dt.format("yyyy") == "0002"
check dt.format("YYYY") == "2"
check dt.format("uuuu") == "-0001"
check dt.format("UUUU") == "-1"
check dt.format("z") == "-1"
check dt.format("zz") == "-01"
check dt.format("zzz") == "-01:02"
check dt.format("zzzz") == "-01:02:03"
check dt.format("g") == "BC"
check dt.format("fff") == "123"
check dt.format("ffffff") == "123456"
check dt.format("fffffffff") == "123456789"
dt.nanosecond = 1
check dt.format("fff") == "000"
check dt.format("ffffff") == "000000"
check dt.format("fffffffff") == "000000001"
dt.year = 12345
check dt.format("yyyy") == "+12345"
check dt.format("uuuu") == "+12345"
dt.year = -12345
check dt.format("yyyy") == "+12346"
check dt.format("uuuu") == "-12345"
expect ValueError:
discard initTimeFormat("'")
expect ValueError:
discard initTimeFormat("'foo")
expect ValueError:
discard initTimeFormat("foo'")
test "parse":
check $parse("20180101", "yyyyMMdd", utc()) == "2018-01-01T00:00:00Z"
parseTestExcp("+120180101", "yyyyMMdd")
check parse("1", "YYYY", utc()).year == 1
check parse("1 BC", "YYYY g", utc()).year == 0
check parse("0001 BC", "yyyy g", utc()).year == 0
check parse("+12345 BC", "yyyy g", utc()).year == -12344
check parse("1 AD", "YYYY g", utc()).year == 1
check parse("0001 AD", "yyyy g", utc()).year == 1
check parse("+12345 AD", "yyyy g", utc()).year == 12345
check parse("-1", "UUUU", utc()).year == -1
check parse("-0001", "uuuu", utc()).year == -1
discard parse("foobar", "'foobar'")
discard parse("foo'bar", "'foo''''bar'")
discard parse("'", "''")
parseTestExcp("2000 A", "yyyy g")

View File

@@ -1,12 +1,12 @@
discard """
output:""
output: ""
"""
doAssert "@[23, 45]" == $(@[23, 45])
doAssert "[32, 45]" == $([32, 45])
doAssert """@["", "foo", "bar"]""" == $(@["", "foo", "bar"])
doAssert """["", "foo", "bar"]""" == $(["", "foo", "bar"])
doAssert """["", "foo", "bar"]""" == $(@["", "foo", "bar"].toOpenArray(0, 2))
doAssert """["", "foo", "bar"]""" == $(["", "foo", "bar"])
doAssert """["", "foo", "bar"]""" == $(@["", "foo", "bar"].toOpenArray(0, 2))
# bug #2395
let alphaSet: set[char] = {'a'..'c'}
@@ -69,13 +69,13 @@ var yy: string
doAssert xx == @[]
doAssert yy == ""
proc bar(arg: cstring): void =
proc bar(arg: cstring) =
doAssert arg[0] == '\0'
proc baz(arg: openarray[char]): void =
proc baz(arg: openarray[char]) =
doAssert arg.len == 0
proc stringCompare(): void =
proc stringCompare() =
var a,b,c,d,e,f,g: string
a.add 'a'
doAssert a == "a"
@@ -102,9 +102,12 @@ proc stringCompare(): void =
doAssert "" != "\0\0\0\0\0\0\0\0\0\0"
var nilstring: string
bar(nilstring)
#bar(nilstring)
baz(nilstring)
stringCompare()
var nilstring: string
bar(nilstring)
static:
stringCompare()

View File

@@ -0,0 +1,33 @@
import gdb
# this test should test the gdb pretty printers of the nim
# library. But be aware this test is not complete. It only tests the
# command line version of gdb. It does not test anything for the
# machine interface of gdb. This means if if this test passes gdb
# frontends might still be broken.
gdb.execute("source ../../../tools/nim-gdb.py")
# debug all instances of the generic function `myDebug`, should be 8
gdb.execute("rbreak myDebug")
gdb.execute("run")
outputs = [
'meTwo',
'"meTwo"',
'{meOne, meThree}',
'MyOtherEnum(1)',
'5',
'array = {1, 2, 3, 4, 5}',
'seq(3, 3) = {"one", "two", "three"}',
'Table(3, 64) = {["two"] = 2, ["three"] = 3, ["one"] = 1}',
]
for i, expected in enumerate(outputs):
if i == 5:
# myArray is passed as pointer to int to myDebug. I look up myArray up in the stack
gdb.execute("up")
output = str(gdb.parse_and_eval("myArray"))
else:
output = str(gdb.parse_and_eval("arg"))
assert output == expected, output + " != " + expected
gdb.execute("continue")

View File

@@ -0,0 +1,3 @@
Loading Nim Runtime support.
NimEnumPrinter: lookup global symbol 'NTI_z9cu80OJCfNgw9bUdzn5ZEzw_ failed for tyEnum_MyOtherEnum_z9cu80OJCfNgw9bUdzn5ZEzw.
8

View File

@@ -0,0 +1,53 @@
import tables
type
MyEnum = enum
meOne,
meTwo,
meThree,
meFour,
MyOtherEnum = enum
moOne,
moTwo,
moThree,
moFoure,
var counter = 0
proc myDebug[T](arg: T): void =
counter += 1
proc testProc(): void =
var myEnum = meTwo
myDebug(myEnum)
# create a string object but also make the NTI for MyEnum is generated
var myString = $myEnum
myDebug(myString)
var mySet = {meOne,meThree}
myDebug(mySet)
# for MyOtherEnum there is no NTI. This tests the fallback for the pretty printer.
var moEnum = moTwo
myDebug(moEnum)
var moSet = {moOne,moThree}
myDebug(moSet)
let myArray = [1,2,3,4,5]
myDebug(myArray)
let mySeq = @["one","two","three"]
myDebug(mySeq)
var myTable = initTable[string, int]()
myTable["one"] = 1
myTable["two"] = 2
myTable["three"] = 3
myDebug(myTable)
echo(counter)
testProc()

View File

@@ -0,0 +1,15 @@
#!/usr/bin/env bash
# Exit if anything fails
set -e
#!/usr/bin/env bash
# Compile the test project with fresh debug information.
nim c --debugger:native gdb_pretty_printer_test_program.nim &> /dev/null
# 2>&1 redirects stderr to stdout (all output in stdout)
# <(...) is a bash feature that makes the output of a command into a
# file handle.
# diff compares the two files, the expected output, and the file
# handle that is created by the execution of gdb.
diff ./gdb_pretty_printer_test_output.txt <(gdb -x gdb_pretty_printer_test.py --batch-silent --args gdb_pretty_printer_test_program 2>&1)
# The exit code of diff is forwarded as the exit code of this
# script. So when the comparison fails, the exit code of this script
# won't be 0. So this script should be embeddable in a test suite.

513
tools/nim-gdb.py Normal file
View File

@@ -0,0 +1,513 @@
import gdb
import re
import sys
# some feedback that the nim runtime support is loading, isn't a bad
# thing at all.
gdb.write("Loading Nim Runtime support.\n", gdb.STDERR)
# When error occure they occur regularly. This 'caches' known errors
# and prevents them from being reprinted over and over again.
errorSet = set()
def printErrorOnce(id, message):
global errorSet
if id not in errorSet:
errorSet.add(id)
gdb.write(message, gdb.STDERR)
nimobjfile = gdb.current_objfile() or gdb.objfiles()[0]
nimobjfile.type_printers = []
################################################################################
##### Type pretty printers
################################################################################
type_hash_regex = re.compile("^\w*_([A-Za-z0-9]*)$")
def getNimRti(type_name):
""" Return a ``gdb.Value`` object for the Nim Runtime Information of ``type_name``. """
# Get static const TNimType variable. This should be available for
# every non trivial Nim type.
m = type_hash_regex.match(type_name)
if m:
try:
return gdb.parse_and_eval("NTI_" + m.group(1) + "_")
except:
return None
class NimTypeRecognizer:
# this type map maps from types that are generated in the C files to
# how they are called in nim. To not mix up the name ``int`` from
# system.nim with the name ``int`` that could still appear in
# generated code, ``NI`` is mapped to ``system.int`` and not just
# ``int``.
type_map_static = {
'NI': 'system.int', 'NI8': 'int8', 'NI16': 'int16', 'NI32': 'int32', 'NI64': 'int64',
'NU': 'uint', 'NU8': 'uint8','NU16': 'uint16', 'NU32': 'uint32', 'NU64': 'uint64',
'NF': 'float', 'NF32': 'float32', 'NF64': 'float64',
'NIM_BOOL': 'bool', 'NIM_CHAR': 'char', 'NCSTRING': 'cstring',
'NimStringDesc': 'string'
}
# Normally gdb distinguishes between the command `ptype` and
# `whatis`. `ptype` prints a very detailed view of the type, and
# `whatis` a very brief representation of the type. I haven't
# figured out a way to know from the type printer that is
# implemented here how to know if a type printer should print the
# short representation or the long representation. As a hacky
# workaround I just say I am not resposible for printing pointer
# types (seq and string are exception as they are semantically
# values). this way the default type printer will handle pointer
# types and dive into the members of that type. So I can still
# control with `ptype myval` and `ptype *myval` if I want to have
# detail or not. I this this method stinks but I could not figure
# out a better solution.
object_type_pattern = re.compile("^(\w*):ObjectType$")
def recognize(self, type_obj):
tname = None
if type_obj.tag is not None:
tname = type_obj.tag
elif type_obj.name is not None:
tname = type_obj.name
# handle pointer types
if not tname:
if type_obj.code == gdb.TYPE_CODE_PTR:
target_type = type_obj.target()
target_type_name = target_type.name
if target_type_name:
# visualize 'string' as non pointer type (unpack pointer type).
if target_type_name == "NimStringDesc":
tname = target_type_name # could also just return 'string'
# visualize 'seq[T]' as non pointer type.
if target_type_name.find('tySequence_') == 0:
tname = target_type_name
if not tname:
# We are not resposible for this type printing.
# Basically this means we don't print pointer types.
return None
result = self.type_map_static.get(tname, None)
if result:
return result
rti = getNimRti(tname)
if rti:
return rti['name'].string("utf-8", "ignore")
else:
return None
class NimTypePrinter:
"""Nim type printer. One printer for all Nim types."""
# enabling and disabling of type printers can be done with the
# following gdb commands:
#
# enable type-printer NimTypePrinter
# disable type-printer NimTypePrinter
name = "NimTypePrinter"
def __init__ (self):
self.enabled = True
def instantiate(self):
return NimTypeRecognizer()
nimobjfile.type_printers = [NimTypePrinter()]
################################################################################
##### GDB Function, equivalent of Nim's $ operator
################################################################################
class DollarPrintFunction (gdb.Function):
"Nim's equivalent of $ operator as a gdb function, available in expressions `print $dollar(myvalue)"
_gdb_dollar_functions = gdb.execute("info functions dollar__", True, True)
dollar_functions = re.findall('NimStringDesc \*(dollar__[A-z0-9_]+?)\(([^,)]*)\);', _gdb_dollar_functions)
def __init__ (self):
super (DollarPrintFunction, self).__init__("dollar")
@staticmethod
def invoke_static(arg):
for func, arg_typ in DollarPrintFunction.dollar_functions:
if arg.type.name == arg_typ:
func_value = gdb.lookup_global_symbol(func, gdb.SYMBOL_FUNCTIONS_DOMAIN).value()
return func_value(arg)
if arg.type.name + " *" == arg_typ:
func_value = gdb.lookup_global_symbol(func, gdb.SYMBOL_FUNCTIONS_DOMAIN).value()
return func_value(arg.address)
typeName = arg.type.name
printErrorOnce(typeName, "No suitable Nim $ operator found for type: " + typeName + ".\n")
def invoke(self, arg):
return self.invoke_static(arg)
DollarPrintFunction()
################################################################################
##### GDB Command, equivalent of Nim's $ operator
################################################################################
class DollarPrintCmd (gdb.Command):
"""Dollar print command for Nim, `$ expr` will invoke Nim's $ operator"""
def __init__ (self):
super (DollarPrintCmd, self).__init__ ("$", gdb.COMMAND_DATA, gdb.COMPLETE_EXPRESSION)
def invoke (self, arg, from_tty):
param = gdb.parse_and_eval(arg)
gdb.write(str(DollarPrintFunction.invoke_static(param)) + "\n", gdb.STDOUT)
DollarPrintCmd()
################################################################################
##### Value pretty printers
################################################################################
class NimBoolPrinter:
pattern = re.compile(r'^NIM_BOOL$')
def __init__(self, val):
self.val = val
def to_string(self):
if self.val == 0:
return "false"
else:
return "true"
################################################################################
class NimStringPrinter:
pattern = re.compile(r'^NimStringDesc \*$')
def __init__(self, val):
self.val = val
def display_hint(self):
return 'string'
def to_string(self):
if self.val:
l = int(self.val['Sup']['len'])
return self.val['data'][0].address.string("utf-8", "ignore", l)
else:
return ""
################################################################################
# proc reprEnum(e: int, typ: PNimType): string {.compilerRtl.} =
# ## Return string representation for enumeration values
# var n = typ.node
# if ntfEnumHole notin typ.flags:
# let o = e - n.sons[0].offset
# if o >= 0 and o <% typ.node.len:
# return $n.sons[o].name
# else:
# # ugh we need a slow linear search:
# var s = n.sons
# for i in 0 .. n.len-1:
# if s[i].offset == e:
# return $s[i].name
# result = $e & " (invalid data!)"
def reprEnum(e, typ):
""" this is a port of the nim runtime function `reprEnum` to python """
e = int(e)
n = typ["node"]
flags = int(typ["flags"])
# 1 << 2 is {ntfEnumHole}
if ((1 << 2) & flags) == 0:
o = e - int(n["sons"][0]["offset"])
if o >= 0 and 0 < int(n["len"]):
return n["sons"][o]["name"].string("utf-8", "ignore")
else:
# ugh we need a slow linear search:
s = n["sons"]
for i in range(0, int(n["len"])):
if int(s[i]["offset"]) == e:
return s[i]["name"].string("utf-8", "ignore")
return str(e) + " (invalid data!)"
class NimEnumPrinter:
pattern = re.compile(r'^tyEnum_(\w*)_([A-Za-z0-9]*)$')
def __init__(self, val):
self.val = val
match = self.pattern.match(self.val.type.name)
self.typeNimName = match.group(1)
typeInfoName = "NTI_" + match.group(2) + "_"
self.nti = gdb.lookup_global_symbol(typeInfoName)
if self.nti is None:
printErrorOnce(typeInfoName, "NimEnumPrinter: lookup global symbol '" + typeInfoName + " failed for " + self.val.type.name + ".\n")
def to_string(self):
if self.nti:
arg0 = self.val
arg1 = self.nti.value(gdb.newest_frame())
return reprEnum(arg0, arg1)
else:
return self.typeNimName + "(" + str(int(self.val)) + ")"
################################################################################
class NimSetPrinter:
## the set printer is limited to sets that fit in an integer. Other
## sets are compiled to `NU8 *` (ptr uint8) and are invisible to
## gdb (currently).
pattern = re.compile(r'^tySet_tyEnum_(\w*)_([A-Za-z0-9]*)$')
def __init__(self, val):
self.val = val
match = self.pattern.match(self.val.type.name)
self.typeNimName = match.group(1)
typeInfoName = "NTI_" + match.group(2) + "_"
self.nti = gdb.lookup_global_symbol(typeInfoName)
if self.nti is None:
printErrorOnce(typeInfoName, "NimSetPrinter: lookup global symbol '"+ typeInfoName +" failed for " + self.val.type.name + ".\n")
def to_string(self):
if self.nti:
nti = self.nti.value(gdb.newest_frame())
enumStrings = []
val = int(self.val)
i = 0
while val > 0:
if (val & 1) == 1:
enumStrings.append(reprEnum(i, nti))
val = val >> 1
i += 1
return '{' + ', '.join(enumStrings) + '}'
else:
return str(int(self.val))
################################################################################
class NimHashSetPrinter:
pattern = re.compile(r'^tyObject_(HashSet)_([A-Za-z0-9]*)$')
def __init__(self, val):
self.val = val
def display_hint(self):
return 'array'
def to_string(self):
counter = 0
capacity = 0
if self.val:
counter = int(self.val['counter'])
if self.val['data']:
capacity = int(self.val['data']['Sup']['len'])
return 'HashSet({0}, {1})'.format(counter, capacity)
def children(self):
if self.val:
data = NimSeqPrinter(self.val['data'])
for idxStr, entry in data.children():
if int(entry['Field0']) > 0:
yield ("data." + idxStr + ".Field1", str(entry['Field1']))
################################################################################
class NimSeqPrinter:
# the pointer is explicity part of the type. So it is part of
# ``pattern``.
pattern = re.compile(r'^tySequence_\w* \*$')
def __init__(self, val):
self.val = val
def display_hint(self):
return 'array'
def to_string(self):
len = 0
cap = 0
if self.val:
len = int(self.val['Sup']['len'])
cap = int(self.val['Sup']['reserved'])
return 'seq({0}, {1})'.format(len, cap)
def children(self):
if self.val:
length = int(self.val['Sup']['len'])
#align = len(str(length - 1))
for i in range(length):
yield ("data[{0}]".format(i), self.val["data"][i])
################################################################################
class NimArrayPrinter:
pattern = re.compile(r'^tyArray_\w*$')
def __init__(self, val):
self.val = val
def display_hint(self):
return 'array'
def to_string(self):
return 'array'
def children(self):
length = self.val.type.sizeof // self.val[0].type.sizeof
align = len(str(length-1))
for i in range(length):
yield ("[{0:>{1}}]".format(i, align), self.val[i])
################################################################################
class NimStringTablePrinter:
pattern = re.compile(r'^tyObject_(StringTableObj)_([A-Za-z0-9]*)(:? \*)?$')
def __init__(self, val):
self.val = val
def display_hint(self):
return 'map'
def to_string(self):
counter = 0
capacity = 0
if self.val:
counter = int(self.val['counter'])
if self.val['data']:
capacity = int(self.val['data']['Sup']['len'])
return 'StringTableObj({0}, {1})'.format(counter, capacity)
def children(self):
if self.val:
data = NimSeqPrinter(self.val['data'])
for idxStr, entry in data.children():
if int(entry['Field2']) > 0:
yield (idxStr + ".Field0", entry['Field0'])
yield (idxStr + ".Field1", entry['Field1'])
################################################################
class NimTablePrinter:
pattern = re.compile(r'^tyObject_(Table)_([A-Za-z0-9]*)(:? \*)?$')
def __init__(self, val):
self.val = val
# match = self.pattern.match(self.val.type.name)
def display_hint(self):
return 'map'
def to_string(self):
counter = 0
capacity = 0
if self.val:
counter = int(self.val['counter'])
if self.val['data']:
capacity = int(self.val['data']['Sup']['len'])
return 'Table({0}, {1})'.format(counter, capacity)
def children(self):
if self.val:
data = NimSeqPrinter(self.val['data'])
for idxStr, entry in data.children():
if int(entry['Field0']) > 0:
yield (idxStr + '.Field1', entry['Field1'])
yield (idxStr + '.Field2', entry['Field2'])
################################################################
# this is untested, therefore disabled
# class NimObjectPrinter:
# pattern = re.compile(r'^tyObject_.*$')
# def __init__(self, val):
# self.val = val
# def display_hint(self):
# return 'object'
# def to_string(self):
# return str(self.val.type)
# def children(self):
# if not self.val:
# yield "object", "<nil>"
# raise StopIteration
# for (i, field) in enumerate(self.val.type.fields()):
# if field.type.code == gdb.TYPE_CODE_UNION:
# yield _union_field
# else:
# yield (field.name, self.val[field])
# def _union_field(self, i, field):
# rti = getNimRti(self.val.type.name)
# if rti is None:
# return (field.name, "UNION field can't be displayed without RTI")
# node_sons = rti['node'].dereference()['sons']
# prev_field = self.val.type.fields()[i - 1]
# descriminant_node = None
# for i in range(int(node['len'])):
# son = node_sons[i].dereference()
# if son['name'].string("utf-8", "ignore") == str(prev_field.name):
# descriminant_node = son
# break
# if descriminant_node is None:
# raise ValueError("Can't find union descriminant field in object RTI")
# if descriminant_node is None: raise ValueError("Can't find union field in object RTI")
# union_node = descriminant_node['sons'][int(self.val[prev_field])].dereference()
# union_val = self.val[field]
# for f1 in union_val.type.fields():
# for f2 in union_val[f1].type.fields():
# if str(f2.name) == union_node['name'].string("utf-8", "ignore"):
# return (str(f2.name), union_val[f1][f2])
# raise ValueError("RTI is absent or incomplete, can't find union definition in RTI")
################################################################################
def makematcher(klass):
def matcher(val):
typeName = str(val.type)
try:
if hasattr(klass, 'pattern') and hasattr(klass, '__name__'):
# print(typeName + " <> " + klass.__name__)
if klass.pattern.match(typeName):
return klass(val)
except Exception as e:
print(klass)
printErrorOnce(typeName, "No matcher for type '" + typeName + "': " + str(e) + "\n")
return matcher
nimobjfile.pretty_printers = []
nimobjfile.pretty_printers.extend([makematcher(var) for var in list(vars().values()) if hasattr(var, 'pattern')])