fix merge conflict

This commit is contained in:
Andreas Rumpf 2018-07-18 09:46:30 +02:00
commit 4389409e26
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 - For string inputs, ``strutils.isUpperAscii`` and ``strutils.isLowerAscii`` now
require a second mandatory parameter ``skipNonAlpha``. require a second mandatory parameter ``skipNonAlpha``.
- ``osLastError`` is now marked with ``sideEffect``
- The procs ``parseHexInt`` and ``parseOctInt`` now fail on empty strings - The procs ``parseHexInt`` and ``parseOctInt`` now fail on empty strings
and strings containing only valid prefixes, e.g. "0x" for hex integers. 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 #### Breaking changes in the compiler

View file

@ -322,7 +322,8 @@ const
usesEffects* = 1 # read effects at position 1 usesEffects* = 1 # read effects at position 1
writeEffects* = 2 # write effects at position 2 writeEffects* = 2 # write effects at position 2
tagEffects* = 3 # user defined tag ('gc', 'time' etc.) 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 type
TTypeKind* = enum # order is important! TTypeKind* = enum # order is important!

View file

@ -27,9 +27,9 @@ proc lineInfoToStr*(conf: ConfigRef; info: TLineInfo): Rope
when declared(echo): when declared(echo):
# these are for debugging only: They are not really deprecated, but I want # these are for debugging only: They are not really deprecated, but I want
# the warning so that release versions do not contain debugging statements: # the warning so that release versions do not contain debugging statements:
proc debug*(n: PSym; conf: ConfigRef = nil) {.deprecated.} proc debug*(n: PSym; conf: ConfigRef = nil) {.exportc: "debugSym", deprecated.}
proc debug*(n: PType; conf: ConfigRef = nil) {.deprecated.} proc debug*(n: PType; conf: ConfigRef = nil) {.exportc: "debugType", deprecated.}
proc debug*(n: PNode; conf: ConfigRef = nil) {.deprecated.} proc debug*(n: PNode; conf: ConfigRef = nil) {.exportc: "debugNode", deprecated.}
template debug*(x: PSym|PType|PNode) {.deprecated.} = template debug*(x: PSym|PType|PNode) {.deprecated.} =
when compiles(c.config): 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 # here for this flag, where it is reasonably safe to do so
# (for objects, etc.): # (for objects, etc.):
if p.config.selectedGC == gcDestructors: if p.config.selectedGC == gcDestructors:
useStringh(p.module)
linefmt(p, cpsStmts, 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)) addrLoc(p.config, dest), addrLoc(p.config, src), rdLoc(dest))
elif needToCopy notin flags or elif needToCopy notin flags or
tfShallow in skipTypes(dest.t, abstractVarRange).flags: tfShallow in skipTypes(dest.t, abstractVarRange).flags:
if dest.storage == OnStack or not usesWriteBarrier(p.config): if dest.storage == OnStack or not usesWriteBarrier(p.config):
useStringh(p.module)
linefmt(p, cpsStmts, 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)) addrLoc(p.config, dest), addrLoc(p.config, src), rdLoc(dest))
else: else:
linefmt(p, cpsStmts, "#genericShallowAssign((void*)$1, (void*)$2, $3);$n", 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): if needsComplexAssignment(dest.t):
genGenericAsgn(p, dest, src, flags) genGenericAsgn(p, dest, src, flags)
else: else:
useStringh(p.module)
linefmt(p, cpsStmts, 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)) rdLoc(dest), rdLoc(src), getTypeDesc(p.module, dest.t))
of tyOpenArray, tyVarargs: of tyOpenArray, tyVarargs:
# open arrays are always on the stack - really? What if a sequence is # 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), addrLoc(p.config, dest), addrLoc(p.config, src),
genTypeInfo(p.module, dest.t, dest.lode.info)) genTypeInfo(p.module, dest.t, dest.lode.info))
else: else:
useStringh(p.module)
linefmt(p, cpsStmts, linefmt(p, cpsStmts,
# bug #4799, keep the memcpy for a while # bug #4799, keep the nimCopyMem for a while
#"memcpy((void*)$1, (NIM_CONST void*)$2, sizeof($1[0])*$1Len_0);$n", #"#nimCopyMem((void*)$1, (NIM_CONST void*)$2, sizeof($1[0])*$1Len_0);$n",
"$1 = $2;$n", "$1 = $2;$n",
rdLoc(dest), rdLoc(src)) rdLoc(dest), rdLoc(src))
of tySet: of tySet:
if mapType(p.config, ty) == ctArray: if mapType(p.config, ty) == ctArray:
useStringh(p.module) linefmt(p, cpsStmts, "#nimCopyMem((void*)$1, (NIM_CONST void*)$2, $3);$n",
linefmt(p, cpsStmts, "memcpy((void*)$1, (NIM_CONST void*)$2, $3);$n",
rdLoc(dest), rdLoc(src), rope(getSize(p.config, dest.t))) rdLoc(dest), rdLoc(src), rope(getSize(p.config, dest.t)))
else: else:
linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src)) 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)) genTypeInfo(p.module, dest.t, dest.lode.info))
of tySet: of tySet:
if mapType(p.config, ty) == ctArray: if mapType(p.config, ty) == ctArray:
useStringh(p.module) linefmt(p, cpsStmts, "#nimCopyMem((void*)$1, (NIM_CONST void*)$2, $3);$n",
linefmt(p, cpsStmts, "memcpy((void*)$1, (NIM_CONST void*)$2, $3);$n",
rdLoc(dest), rdLoc(src), rope(getSize(p.config, dest.t))) rdLoc(dest), rdLoc(src), rope(getSize(p.config, dest.t)))
else: else:
linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src)) 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)") if op == mHigh: unaryExpr(p, e, d, "($1Len_0-1)")
else: unaryExpr(p, e, d, "$1Len_0") else: unaryExpr(p, e, d, "$1Len_0")
of tyCString: of tyCString:
useStringh(p.module) if op == mHigh: unaryExpr(p, e, d, "($1 ? (#nimCStrLen($1)-1) : -1)")
if op == mHigh: unaryExpr(p, e, d, "($1 ? (strlen($1)-1) : -1)") else: unaryExpr(p, e, d, "($1 ? #nimCStrLen($1) : 0)")
else: unaryExpr(p, e, d, "($1 ? strlen($1) : 0)")
of tyString: of tyString:
if not p.module.compileToCpp: if not p.module.compileToCpp:
if op == mHigh: unaryExpr(p, e, d, "($1 ? ($1->Sup.len-1) : -1)") 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" & " $3 = (($4[$1] & ~ $5[$1]) == 0);$n" &
" if (!$3) break;}$n", "for ($1 = 0; $1 < $2; $1++) { $n" & " if (!$3) break;}$n", "for ($1 = 0; $1 < $2; $1++) { $n" &
" $3 = (($4[$1] & ~ $5[$1]) == 0);$n" & " if (!$3) break;}$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 a, b, i: TLoc
var setType = skipTypes(e.sons[1].typ, abstractVar) 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[1], a)
initLocExpr(p, e.sons[2], b) initLocExpr(p, e.sons[2], b)
if d.k == locNone: getTemp(p, getSysType(p.module.g.graph, unknownLineInfo(), tyBool), d) 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)]) [rdLoc(i), rope(size), rdLoc(d), rdLoc(a), rdLoc(b)])
of mEqSet: of mEqSet:
useStringh(p.module) binaryExprChar(p, e, d, "(#nimCmpMem($1, $2, " & $(size) & ")==0)")
binaryExprChar(p, e, d, "(memcmp($1, $2, " & $(size) & ")==0)")
of mMulSet, mPlusSet, mMinusSet, mSymDiffSet: of mMulSet, mPlusSet, mMinusSet, mSymDiffSet:
# we inline the simple for loop for better code generation: # we inline the simple for loop for better code generation:
getTemp(p, getSysType(p.module.g.graph, unknownLineInfo(), tyInt), i) # our counter 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) = proc genSetConstr(p: BProc, e: PNode, d: var TLoc) =
# example: { a..b, c, d, e, f..g } # example: { a..b, c, d, e, f..g }
# we have to emit an expression of the form: # 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); # incl(tmp, d); incl(tmp, e); inclRange(tmp, f, g);
var var
a, b, idx: TLoc 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 d.k == locNone: getTemp(p, e.typ, d)
if getSize(p.config, e.typ) > 8: if getSize(p.config, e.typ) > 8:
# big set: # big set:
useStringh(p.module) linefmt(p, cpsStmts, "#nimZeroMem($1, sizeof($2));$n",
lineF(p, cpsStmts, "memset($1, 0, sizeof($2));$n",
[rdLoc(d), getTypeDesc(p.module, e.typ)]) [rdLoc(d), getTypeDesc(p.module, e.typ)])
for it in e.sons: for it in e.sons:
if it.kind == nkRange: if it.kind == nkRange:

View file

@ -1146,5 +1146,9 @@ proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) =
proc genStmts(p: BProc, t: PNode) = proc genStmts(p: BProc, t: PNode) =
var a: TLoc var a: TLoc
let isPush = hintExtendedContext in p.config.notes
if isPush: pushInfoContext(p.config, t.info)
expr(p, t, a) expr(p, t, a)
if isPush: popInfoContext(p.config)
internalAssert p.config, a.k in {locNone, locTemp, locLocalVar} 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 result = s.loc.r
if result == nil: if result == nil:
result = s.name.s.mangle.rope 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 s.loc.r = result
writeMangledName(m.ndi, s, m.config) writeMangledName(m.ndi, s, m.config)

View file

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

View file

@ -16,7 +16,7 @@ import
wordrecg, syntaxes, renderer, lexer, packages/docutils/rstast, wordrecg, syntaxes, renderer, lexer, packages/docutils/rstast,
packages/docutils/rst, packages/docutils/rstgen, times, packages/docutils/rst, packages/docutils/rstgen, times,
packages/docutils/highlite, sempass2, json, xmltree, cgi, packages/docutils/highlite, sempass2, json, xmltree, cgi,
typesrenderer, astalgo, modulepaths, lineinfos typesrenderer, astalgo, modulepaths, lineinfos, sequtils
type type
TSections = array[TSymKind, Rope] TSections = array[TSymKind, Rope]
@ -259,11 +259,19 @@ proc nodeToHighlightedHtml(d: PDoc; n: PNode; result: var Rope; renderFlags: TRe
of tkSpaces, tkInvalid: of tkSpaces, tkInvalid:
add(result, literal) add(result, literal)
of tkCurlyDotLe: of tkCurlyDotLe:
dispA(d.conf, result, """<span 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}", "\\spanOther{$1}",
[rope(esc(d.target, literal))]) [rope(esc(d.target, literal))])
of tkCurlyDotRi: 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}", "\\spanOther{$1}",
[rope(esc(d.target, literal))]) [rope(esc(d.target, literal))])
of tkParLe, tkParRi, tkBracketLe, tkBracketRi, tkCurlyLe, tkCurlyRi, of tkParLe, tkParRi, tkBracketLe, tkBracketRi, tkCurlyLe, tkCurlyRi,
@ -280,7 +288,7 @@ proc getAllRunnableExamples(d: PDoc; n: PNode; dest: var Rope) =
of nkCallKinds: of nkCallKinds:
if n[0].kind == nkSym and n[0].sym.magic == mRunnableExamples and if n[0].kind == nkSym and n[0].sym.magic == mRunnableExamples and
n.len >= 2 and n.lastSon.kind == nkStmtList: 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:"]) "\n\\textbf{$1}\n", [rope"Examples:"])
inc d.listingCounter inc d.listingCounter
let id = $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 = proc toObjFile*(conf: ConfigRef; filename: string): string =
# Object file for compilation # Object file for compilation
#if filename.endsWith(".cpp"): result = filename & "." & CC[conf.cCompiler].objExt
# result = changeFileExt(filename, "cpp." & CC[cCompiler].objExt)
#else:
result = changeFileExt(filename, CC[conf.cCompiler].objExt)
proc addFileToCompile*(conf: ConfigRef; cf: Cfile) = proc addFileToCompile*(conf: ConfigRef; cf: Cfile) =
conf.toCompile.add(cf) conf.toCompile.add(cf)

View file

@ -45,7 +45,8 @@ type
hintExecuting, hintLinking, hintDependency, hintExecuting, hintLinking, hintDependency,
hintSource, hintPerformance, hintStackTrace, hintGCStats, hintSource, hintPerformance, hintStackTrace, hintGCStats,
hintGlobalVar, hintGlobalVar,
hintUser, hintUserRaw hintUser, hintUserRaw,
hintExtendedContext
const const
MsgKindToStr*: array[TMsgKind, string] = [ MsgKindToStr*: array[TMsgKind, string] = [
@ -116,7 +117,9 @@ const
hintGCStats: "$1", hintGCStats: "$1",
hintGlobalVar: "global variable declared here", hintGlobalVar: "global variable declared here",
hintUser: "$1", hintUser: "$1",
hintUserRaw: "$1"] hintUserRaw: "$1",
hintExtendedContext: "$1",
]
const const
WarningsToStr* = ["CannotOpenFile", "OctalEscape", WarningsToStr* = ["CannotOpenFile", "OctalEscape",
@ -132,12 +135,14 @@ const
"GcMem", "Destructor", "LockLevel", "ResultShadowed", "GcMem", "Destructor", "LockLevel", "ResultShadowed",
"Spacing", "User"] "Spacing", "User"]
HintsToStr* = ["Success", "SuccessX", "LineTooLong", HintsToStr* = [
"Success", "SuccessX", "LineTooLong",
"XDeclaredButNotUsed", "ConvToBaseNotNeeded", "ConvFromXtoItselfNotNeeded", "XDeclaredButNotUsed", "ConvToBaseNotNeeded", "ConvFromXtoItselfNotNeeded",
"ExprAlwaysX", "QuitCalled", "Processing", "CodeBegin", "CodeEnd", "Conf", "ExprAlwaysX", "QuitCalled", "Processing", "CodeBegin", "CodeEnd", "Conf",
"Path", "CondTrue", "Name", "Pattern", "Exec", "Link", "Dependency", "Path", "CondTrue", "Name", "Pattern", "Exec", "Link", "Dependency",
"Source", "Performance", "StackTrace", "GCStats", "GlobalVar", "Source", "Performance", "StackTrace", "GCStats", "GlobalVar",
"User", "UserRaw"] "User", "UserRaw", "ExtendedContext",
]
const const
fatalMin* = errUnknown fatalMin* = errUnknown
@ -157,30 +162,17 @@ type
TNoteKind* = range[warnMin..hintMax] # "notes" are warnings or hints TNoteKind* = range[warnMin..hintMax] # "notes" are warnings or hints
TNoteKinds* = set[TNoteKind] TNoteKinds* = set[TNoteKind]
const proc computeNotesVerbosity(): array[0..3, TNoteKinds] =
NotesVerbosity*: array[0..3, TNoteKinds] = [ result[3] = {low(TNoteKind)..high(TNoteKind)} - {}
{low(TNoteKind)..high(TNoteKind)} - {warnShadowIdent, warnUninit, result[2] = result[3] - {hintStackTrace, warnUninit, hintExtendedContext}
warnProveField, warnProveIndex, result[1] = result[2] - {warnShadowIdent, warnProveField, warnProveIndex,
warnGcUnsafe, warnGcUnsafe, hintPath, hintDependency, hintCodeBegin, hintCodeEnd,
hintSuccessX, hintPath, hintConf, hintSource, hintGlobalVar, hintGCStats}
hintProcessing, hintPattern, result[0] = result[1] - {hintSuccessX, hintConf, hintProcessing,
hintDependency, hintPattern, hintExecuting, hintLinking}
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)}]
const const
NotesVerbosity* = computeNotesVerbosity()
errXMustBeCompileTime* = "'$1' can only be used in compile-time context" errXMustBeCompileTime* = "'$1' can only be used in compile-time context"
errArgsNeedRunOption* = "arguments can only be given if the '--run' option is selected" 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) put(g, tkComment, com)
com = "## " com = "## "
inc(i) inc(i)
if i < s.len and s[i] == '\x0A': inc(i) if i <= hi and s[i] == '\x0A': inc(i)
optNL(g, ind) optNL(g, ind)
of '\x0A': of '\x0A':
put(g, tkComment, com) put(g, tkComment, com)
@ -226,7 +226,7 @@ proc maxLineLength(s: string): int =
break break
of '\x0D': of '\x0D':
inc(i) inc(i)
if s[i] == '\x0A': inc(i) if i <= hi and s[i] == '\x0A': inc(i)
result = max(result, lineLen) result = max(result, lineLen)
lineLen = 0 lineLen = 0
of '\x0A': of '\x0A':
@ -247,7 +247,7 @@ proc putRawStr(g: var TSrcGen, kind: TTokType, s: string) =
put(g, kind, str) put(g, kind, str)
str = "" str = ""
inc(i) inc(i)
if (i <= hi) and (s[i] == '\x0A'): inc(i) if i <= hi and s[i] == '\x0A': inc(i)
optNL(g, 0) optNL(g, 0)
of '\x0A': of '\x0A':
put(g, kind, str) 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 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 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 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 nkCommand: result = lsub(g, n.sons[0]) + lcomma(g, n, 1) + 1
of nkExprEqExpr, nkAsgn, nkFastAsgn: result = lsons(g, n) + 3 of nkExprEqExpr, nkAsgn, nkFastAsgn: result = lsons(g, n) + 3
of nkPar, nkCurly, nkBracket, nkClosure: result = lcomma(g, n) + 2 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 nkChckRangeF: result = len("chckRangeF") + 2 + lcomma(g, n)
of nkChckRange64: result = len("chckRange64") + 2 + lcomma(g, n) of nkChckRange64: result = len("chckRange64") + 2 + lcomma(g, n)
of nkChckRange: result = len("chckRange") + 2 + lcomma(g, n) of nkChckRange: result = len("chckRange") + 2 + lcomma(g, n)
of nkObjDownConv, nkObjUpConv, nkStringToCString, nkCStringToString: of nkObjDownConv, nkObjUpConv:
result = 2 result = 2
if sonsLen(n) >= 1: result = result + lsub(g, n.sons[0]) if sonsLen(n) >= 1: result = result + lsub(g, n.sons[0])
result = result + lcomma(g, n, 1) result = result + lcomma(g, n, 1)
@ -968,7 +968,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
put(g, tkParLe, "(") put(g, tkParLe, "(")
gcomma(g, n) gcomma(g, n)
put(g, tkParRi, ")") put(g, tkParRi, ")")
of nkObjDownConv, nkObjUpConv, nkStringToCString, nkCStringToString: of nkObjDownConv, nkObjUpConv:
if sonsLen(n) >= 1: gsub(g, n.sons[0]) if sonsLen(n) >= 1: gsub(g, n.sons[0])
put(g, tkParLe, "(") put(g, tkParLe, "(")
gcomma(g, n, 1) gcomma(g, n, 1)
@ -1020,7 +1020,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
of nkBind: of nkBind:
putWithSpace(g, tkBind, "bind") putWithSpace(g, tkBind, "bind")
gsub(g, n, 0) gsub(g, n, 0)
of nkCheckedFieldExpr, nkHiddenAddr, nkHiddenDeref: of nkCheckedFieldExpr, nkHiddenAddr, nkHiddenDeref, nkStringToCString, nkCStringToString:
gsub(g, n, 0) gsub(g, n, 0)
of nkLambda: of nkLambda:
putWithSpace(g, tkProc, "proc") 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 == nkSym: n[0].sym.name
elif n[0].kind in {nkOpenSymChoice, nkClosedSymChoice}: n[0][0].sym.name elif n[0].kind in {nkOpenSymChoice, nkClosedSymChoice}: n[0][0].sym.name
else: nil 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) put(g, tkSpaces, Space)
if n.sons[1].kind == nkInfix: if n_next.kind == nkInfix:
put(g, tkParLe, "(") put(g, tkParLe, "(")
gsub(g, n.sons[1]) gsub(g, n.sons[1])
put(g, tkParRi, ")") put(g, tkParRi, ")")

View file

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

View file

@ -907,8 +907,11 @@ proc buildEchoStmt(c: PContext, n: PNode): PNode =
result = semExpr(c, result) result = semExpr(c, result)
proc semExprNoType(c: PContext, n: PNode): PNode = 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}) result = semExpr(c, n, {efWantStmt})
discardCheck(c, result) discardCheck(c, result)
if isPush: popInfoContext(c.config)
proc isTypeExpr(n: PNode): bool = proc isTypeExpr(n: PNode): bool =
case n.kind case n.kind

View file

@ -171,7 +171,7 @@ proc semTypeTraits(c: PContext, n: PNode): PNode =
proc semOrd(c: PContext, n: PNode): PNode = proc semOrd(c: PContext, n: PNode): PNode =
result = n result = n
let parType = n.sons[1].typ let parType = n.sons[1].typ
if isOrdinalType(parType): if isOrdinalType(parType, allowEnumWithHoles=true):
discard discard
elif parType.kind == tySet: elif parType.kind == tySet:
result.typ = makeRangeType(c, firstOrd(c.config, parType), lastOrd(c.config, parType), n.info) 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 # XXX prove the soundness of this effect system rule
result = n.kind == nkSym and n.sym.kind == skParam and owner == n.sym.owner 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) = proc trackOperand(tracked: PEffects, n: PNode, paramType: PType) =
let a = skipConvAndClosure(n) let a = skipConvAndClosure(n)
let op = a.typ let op = a.typ
@ -561,7 +565,7 @@ proc trackOperand(tracked: PEffects, n: PNode, paramType: PType) =
let s = n.skipConv let s = n.skipConv
if s.kind == nkSym and s.sym.kind in routineKinds: if s.kind == nkSym and s.sym.kind in routineKinds:
propagateEffects(tracked, n, s.sym) propagateEffects(tracked, n, s.sym)
elif effectList.len == 0: elif isNoEffectList(effectList):
if isForwardedProc(n): if isForwardedProc(n):
# we have no explicit effects but it's a forward declaration and so it's # 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: # 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] var effectList = op.n.sons[0]
if a.kind == nkSym and a.sym.kind == skMethod: if a.kind == nkSym and a.sym.kind == skMethod:
propagateEffects(tracked, n, a.sym) propagateEffects(tracked, n, a.sym)
elif effectList.len == 0: elif isNoEffectList(effectList):
if isForwardedProc(a): if isForwardedProc(a):
propagateEffects(tracked, n, a.sym) propagateEffects(tracked, n, a.sym)
elif isIndirectCall(a, tracked.owner): elif isIndirectCall(a, tracked.owner):
@ -781,6 +785,15 @@ proc track(tracked: PEffects, n: PNode) =
initVar(tracked, child.sons[i], volatileCheck=false) initVar(tracked, child.sons[i], volatileCheck=false)
addAsgnFact(tracked.guards, child.sons[i], last) addAsgnFact(tracked.guards, child.sons[i], last)
notNilCheck(tracked, last, child.sons[i].typ) 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 # since 'var (a, b): T = ()' is not even allowed, there is always type
# inference for (a, b) and thus no nil checking is necessary. # inference for (a, b) and thus no nil checking is necessary.
of nkConstSection: of nkConstSection:
@ -897,19 +910,18 @@ proc checkMethodEffects*(g: ModuleGraph; disp, branch: PSym) =
[$branch.typ.lockLevel, $disp.typ.lockLevel]) [$branch.typ.lockLevel, $disp.typ.lockLevel])
proc setEffectsForProcType*(g: ModuleGraph; t: PType, n: PNode) = 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 if t.kind != tyProc or effects.kind != nkEffectList: return
if n.kind != nkEmpty:
let
raisesSpec = effectSpec(n, wRaises)
tagsSpec = effectSpec(n, wTags)
if not isNil(raisesSpec) or not isNil(tagsSpec):
internalAssert g.config, effects.len == 0 internalAssert g.config, effects.len == 0
newSeq(effects.sons, effectListLen) newSeq(effects.sons, effectListLen)
let raisesSpec = effectSpec(n, wRaises)
if not isNil(raisesSpec): if not isNil(raisesSpec):
effects.sons[exceptionEffects] = raisesSpec effects[exceptionEffects] = raisesSpec
let tagsSpec = effectSpec(n, wTags)
if not isNil(tagsSpec): 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) = proc initEffects(g: ModuleGraph; effects: PNode; s: PSym; t: var TEffects) =
newSeq(effects.sons, effectListLen) 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[tagEffects] = newNodeI(nkArgList, s.info)
effects.sons[usesEffects] = g.emptyNode effects.sons[usesEffects] = g.emptyNode
effects.sons[writeEffects] = g.emptyNode effects.sons[writeEffects] = g.emptyNode
effects.sons[pragmasEffects] = g.emptyNode
t.exc = effects.sons[exceptionEffects] t.exc = effects.sons[exceptionEffects]
t.tags = effects.sons[tagEffects] t.tags = effects.sons[tagEffects]

View file

@ -557,7 +557,10 @@ proc semCaseBranch(c: PContext, t, branch: PNode, branchIndex: int,
return return
elif r.kind notin {nkCurly, nkBracket} or len(r) == 0: elif r.kind notin {nkCurly, nkBracket} or len(r) == 0:
checkMinSonsLen(t, 1, c.config) 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) inc(covered)
else: else:
if r.kind == nkCurly: if r.kind == nkCurly:
@ -1341,6 +1344,10 @@ proc semTypeClass(c: PContext, n: PNode, prev: PType): PType =
dummyName = param dummyName = param
dummyType = candidateTypeSlot 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 internalAssert c.config, dummyName.kind == nkIdent
var dummyParam = newSym(if modifier == tyTypeDesc: skType else: skVar, var dummyParam = newSym(if modifier == tyTypeDesc: skType else: skVar,
dummyName.ident, owner, param.info) 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 c.hashType t.sons[i], flags
else: else:
c.hashType t.lastSon, flags c.hashType t.lastSon, flags
of tyAlias, tySink, tyUserTypeClasses: of tyAlias, tySink, tyUserTypeClasses, tyInferred:
c.hashType t.lastSon, flags c.hashType t.lastSon, flags
of tyBool, tyChar, tyInt..tyUInt64: of tyBool, tyChar, tyInt..tyUInt64:
# no canonicalization for integral types, so that e.g. ``pid_t`` is # 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 # signature. There is a change that the target
# type is already fully-determined, so we are # type is already fully-determined, so we are
# going to try resolve it # 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: if f == nil or f.isMetaType:
# no luck resolving the type, so the inference fails # no luck resolving the type, so the inference fails
return isBothMetaConvertible return isBothMetaConvertible
@ -637,7 +640,7 @@ proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
else: discard else: discard
proc typeRangeRel(f, a: PType): TTypeRelation {.noinline.} = 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: if a0 == f0 and a1 == f1:
isEqual isEqual
elif a0 >= f0 and a1 <= f1: elif a0 >= f0 and a1 <= f1:
@ -647,12 +650,12 @@ proc typeRangeRel(f, a: PType): TTypeRelation {.noinline.} =
isConvertible isConvertible
else: else:
isNone isNone
if f.isOrdinalType: if f.isOrdinalType:
checkRange(firstOrd(nil, a), lastOrd(nil, a), firstOrd(nil, f), lastOrd(nil, f)) checkRange(firstOrd(nil, a), lastOrd(nil, a), firstOrd(nil, f), lastOrd(nil, f))
else: else:
checkRange(firstFloat(a), lastFloat(a), firstFloat(f), lastFloat(f)) checkRange(firstFloat(a), lastFloat(a), firstFloat(f), lastFloat(f))
proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType = proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType =
var var
@ -1801,7 +1804,7 @@ proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType,
# 'f <- dest' in order to not break the unification: # 'f <- dest' in order to not break the unification:
# see tests/tgenericconverter: # see tests/tgenericconverter:
let srca = typeRel(m, src, a) let srca = typeRel(m, src, a)
if srca notin {isEqual, isGeneric}: continue if srca notin {isEqual, isGeneric, isSubtype}: continue
let destIsGeneric = containsGenericType(dest) let destIsGeneric = containsGenericType(dest)
if destIsGeneric: if destIsGeneric:
@ -1814,7 +1817,12 @@ proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType,
s.info = arg.info s.info = arg.info
result = newNodeIT(nkHiddenCallConv, arg.info, dest) result = newNodeIT(nkHiddenCallConv, arg.info, dest)
addSon(result, s) 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) inc(m.convMatches)
m.genericConverter = srca == isGeneric or destIsGeneric m.genericConverter = srca == isGeneric or destIsGeneric
return result return result
@ -2231,6 +2239,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
m.state = csNoMatch m.state = csNoMatch
return return
m.baseTypeMatch = false m.baseTypeMatch = false
m.typedescMatched = false
n.sons[a].sons[1] = prepareOperand(c, formal.typ, n.sons[a].sons[1]) n.sons[a].sons[1] = prepareOperand(c, formal.typ, n.sons[a].sons[1])
n.sons[a].typ = n.sons[a].sons[1].typ n.sons[a].typ = n.sons[a].sons[1].typ
var arg = paramTypesMatch(m, formal.typ, n.sons[a].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 # beware of the side-effects in 'prepareOperand'! So only do it for
# varargs matching. See tests/metatype/tstatic_overloading. # varargs matching. See tests/metatype/tstatic_overloading.
m.baseTypeMatch = false m.baseTypeMatch = false
m.typedescMatched = false
incl(marker, formal.position) incl(marker, formal.position)
n.sons[a] = prepareOperand(c, formal.typ, n.sons[a]) n.sons[a] = prepareOperand(c, formal.typ, n.sons[a])
var arg = paramTypesMatch(m, formal.typ, n.sons[a].typ, 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]) addSon(container, n.sons[a])
else: else:
m.baseTypeMatch = false m.baseTypeMatch = false
m.typedescMatched = false
n.sons[a] = prepareOperand(c, formal.typ, n.sons[a]) n.sons[a] = prepareOperand(c, formal.typ, n.sons[a])
var arg = paramTypesMatch(m, formal.typ, n.sons[a].typ, var arg = paramTypesMatch(m, formal.typ, n.sons[a].typ,
n.sons[a], nOrig.sons[a]) n.sons[a], nOrig.sons[a])

View file

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

View file

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

View file

@ -229,7 +229,8 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo;
for i in 1..<t.sons.len: for i in 1..<t.sons.len:
fp.add newIdentDefs(t.n[i], t.sons[i]) fp.add newIdentDefs(t.n[i], t.sons[i])
result.add fp 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: else:
result = mapTypeToBracket("proc", mNone, t, info) result = mapTypeToBracket("proc", mNone, t, info)
of tyOpenArray: result = mapTypeToBracket("openArray", mOpenArray, t, info) of tyOpenArray: result = mapTypeToBracket("openArray", mOpenArray, t, info)

View file

@ -111,10 +111,10 @@ path="$lib/pure"
@if nintendoswitch: @if nintendoswitch:
cc = "switch_gcc" cc = "switch_gcc"
switch_gcc.options.linker = "-g -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE $SWITCH_LIBS" 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_LIBS" 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 $SWITCH_INCLUDES -D__SWITCH__" 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 $SWITCH_INCLUDES -D__SWITCH__ -fno-rtti -fno-exceptions -std=gnu++11" 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 @end
# Configuration for the Intel C/C++ compiler: # Configuration for the Intel C/C++ compiler:

View file

@ -1330,15 +1330,6 @@ dt pre > span.Operator ~ span.Identifier {
background-repeat: no-repeat; 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=="); 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; } margin-bottom: -5px; }
div.pragma {
display: none;
}
span.pragmabegin {
cursor: pointer;
}
span.pragmaend {
cursor: pointer;
}
div.search_results { div.search_results {
background-color: antiquewhite; background-color: antiquewhite;
@ -1351,32 +1342,47 @@ div#global-links ul {
margin-left: 0; margin-left: 0;
list-style-type: none; 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> </style>
<script type="text/javascript" src="../dochack.js"></script> <script type="text/javascript" src="../dochack.js"></script>
<script type="text/javascript"> <script type="text/javascript">
function togglepragma(d) {
if (d.style.display != 'inline')
d.style.display = 'inline';
else
d.style.display = 'none';
}
function main() { function main() {
var elements = document.getElementsByClassName("pragmabegin"); var pragmaDots = document.getElementsByClassName("pragmadots");
for (var i = 0; i < elements.length; ++i) { for (var i = 0; i < pragmaDots.length; i++) {
var e = elements[i]; pragmaDots[i].onclick = function(event) {
e.onclick = function(event) { // Hide tease
togglepragma(event.target.nextSibling); event.target.parentNode.style.display = "none";
}; // Show actual
} event.target.parentNode.nextElementSibling.style.display = "inline";
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);
};
} }
} }
</script> </script>

View file

@ -1270,10 +1270,10 @@ AST:
nnkIdent("float32"), nnkIdent("float32"),
nnkEmpty() nnkEmpty()
) )
nnkPragma(nnkIdent("inline")), ),
nnkEmpty(), # reserved slot for future use nnkPragma(nnkIdent("inline")),
nnkStmtList(nnkDiscardStmt(nnkEmpty())) # the meat of the proc nnkEmpty(), # reserved slot for future use
) nnkStmtList(nnkDiscardStmt(nnkEmpty())) # the meat of the proc
) )
There is another consideration. Nim has flexible type identification for 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 -p, --path:PATH add path to search paths
-d, --define:SYMBOL(:VAL) -d, --define:SYMBOL(:VAL)
define a conditional symbol 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 -u, --undef:SYMBOL undefine a conditional symbol
-f, --forceBuild force rebuilding of all modules -f, --forceBuild force rebuilding of all modules
--stackTrace:on|off turn stack tracing on|off --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. letter allows common code like ``var foo: Foo`` to be parsed unambiguously.
Historically, Nim was a fully `style-insensitive`:idx: language. This meant that 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``. distinction between ``foo`` and ``Foo``.

View file

@ -232,12 +232,12 @@ Cross compilation for Nintendo Switch
===================================== =====================================
Simply add --os:nintendoswitch Simply add --os:nintendoswitch
to your usual ``nim c`` or ``nim cpp`` command and set the ``SWITCH_LIBS`` to your usual ``nim c`` or ``nim cpp`` command and set the ``passC``
and ``SWITCH_INCLUDES`` environment variables to something like: and ``passL`` command line switches to something like:
.. code-block:: console .. code-block:: console
export SWITCH_INCLUDES="-I$DEVKITPRO/libnx/include" nim c ... --passC="-I$DEVKITPRO/libnx/include" ...
export SWITCH_LIBS="-specs=$DEVKITPRO/libnx/switch.specs -L$DEVKITPRO/libnx/lib -lnx" --passL="-specs=$DEVKITPRO/libnx/switch.specs -L$DEVKITPRO/libnx/lib -lnx"
or setup a nim.cfg file like so: 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 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>`_. `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. There are a few things that don't work because the DevkitPro libraries don't support them.
They are: They are:

View file

@ -891,7 +891,7 @@ important differences:
future version of the compiler.) future version of the compiler.)
However, you can also use a ``closure`` iterator to get a different set of 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 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 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 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 let
typ = n.getTypeInst() 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() let impl = typ[1].getImpl()
if impl[0].kind == nnkPragmaExpr: if impl[0].kind == nnkPragmaExpr:
return impl[0][1] return impl[0][1]

View file

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

View file

@ -122,6 +122,21 @@ var
B9600* {.importc, header: "<termios.h>".}: Speed B9600* {.importc, header: "<termios.h>".}: Speed
B19200* {.importc, header: "<termios.h>".}: Speed B19200* {.importc, header: "<termios.h>".}: Speed
B38400* {.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 EXTA* {.importc, header: "<termios.h>".}: Speed
EXTB* {.importc, header: "<termios.h>".}: Speed EXTB* {.importc, header: "<termios.h>".}: Speed
CSIZE* {.importc, header: "<termios.h>".}: Cflag CSIZE* {.importc, header: "<termios.h>".}: Cflag

View file

@ -1890,7 +1890,7 @@ proc entityToUtf8*(entity: string): string =
proc addNode(father, son: XmlNode) = proc addNode(father, son: XmlNode) =
if son != nil: add(father, son) 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 = proc expected(x: var XmlParser, n: XmlNode): string =
result = errorMsg(x, "</" & n.tag & "> expected") result = errorMsg(x, "</" & n.tag & "> expected")

View file

@ -66,7 +66,7 @@ proc raiseOSError*(errorCode: OSErrorCode; additionalInfo = "") {.noinline.} =
raise e raise e
{.push stackTrace:off.} {.push stackTrace:off.}
proc osLastError*(): OSErrorCode = proc osLastError*(): OSErrorCode {.sideEffect.} =
## Retrieves the last operating system error code. ## Retrieves the last operating system error code.
## ##
## This procedure is useful in the event when an OS call fails. In that case ## 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 = proc `==`*(x, y: MemSlice): bool =
## Compare a pair of MemSlice for strict equality. ## 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 equalMem(x.data, y.data, x.size))
result = (x.size == y.size and memcmp(x.data, y.data, x.size) == 0)
proc `$`*(ms: MemSlice): string {.inline.} = proc `$`*(ms: MemSlice): string {.inline.} =
## Return a Nim string built from a MemSlice. ## Return a Nim string built from a MemSlice.

View file

@ -189,7 +189,7 @@ proc `$`*[T](self: Option[T]): string =
if self.isSome: if self.isSome:
"Some(" & $self.val & ")" "Some(" & $self.val & ")"
else: else:
"None[" & T.name & "]" "None[" & name(T) & "]"
when isMainModule: when isMainModule:
import unittest, sequtils import unittest, sequtils
@ -298,3 +298,17 @@ when isMainModule:
test "none[T]": test "none[T]":
check(none[int]().isNone) check(none[int]().isNone)
check(none(int) == none[int]()) 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: else:
if chdir(newDir) != 0'i32: raiseOSError(osLastError()) 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", proc expandFilename*(filename: string): string {.rtl, extern: "nos$1",
tags: [ReadDirEffect].} = tags: [ReadDirEffect].} =
## Returns the full (`absolute`:idx:) path of an existing file `filename`, ## 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. ## Checks whether a given `path` is absolute.
## ##
## On Windows, network paths are considered absolute too. ## 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: when doslikeFileSystem:
var len = len(path) 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] == ':') (len > 1 and path[0] in {'a'..'z', 'A'..'Z'} and path[1] == ':')
elif defined(macos): 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): elif defined(RISCOS):
result = path[0] == '$' result = path[0] == '$'
elif defined(posix): elif defined(posix):

View file

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

View file

@ -19,22 +19,30 @@
import macros import macros
import strformat import strformat
from strutils import toLowerAscii from strutils import toLowerAscii
import colors import colors, tables
const when defined(windows):
hasThreadSupport = compileOption("threads") import winlean
when not hasThreadSupport: type
import tables PTerminal = ref object
var trueColorIsSupported: bool
colorsFGCache = initTable[Color, string]() trueColorIsEnabled: bool
colorsBGCache = initTable[Color, string]() fgSetColor: bool
styleCache = initTable[int, string]() when defined(windows):
hStdout: Handle
hStderr: Handle
oldStdoutAttr: int16
oldStderrAttr: int16
var var gTerm {.threadvar.}: PTerminal
trueColorIsSupported: bool
trueColorIsEnabled: bool proc newTerminal(): PTerminal
fgSetColor: bool
proc getTerminal(): PTerminal {.inline.} =
if isNil(gTerm):
gTerm = newTerminal()
result = gTerm
const const
fgPrefix = "\x1b[38;2;" fgPrefix = "\x1b[38;2;"
@ -156,23 +164,6 @@ when defined(windows):
proc setConsoleMode(hConsoleHandle: Handle, dwMode: DWORD): WINBOOL{. proc setConsoleMode(hConsoleHandle: Handle, dwMode: DWORD): WINBOOL{.
stdcall, dynlib: "kernel32", importc: "SetConsoleMode".} 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] = proc getCursorPos(h: Handle): tuple [x,y: int] =
var c: CONSOLESCREENBUFFERINFO var c: CONSOLESCREENBUFFERINFO
if getConsoleScreenBufferInfo(h, addr(c)) == 0: if getConsoleScreenBufferInfo(h, addr(c)) == 0:
@ -193,12 +184,23 @@ when defined(windows):
return c.wAttributes return c.wAttributes
return 0x70'i16 # ERROR: return white background, black text return 0x70'i16 # ERROR: return white background, black text
var proc initTerminal(term: PTerminal) =
oldStdoutAttr = getAttributes(hStdout) var hStdoutTemp = getStdHandle(STD_OUTPUT_HANDLE)
oldStderrAttr = getAttributes(hStderr) 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 = template conHandle(f: File): Handle =
if f == stderr: hStderr else: hStdout let term = getTerminal()
if f == stderr: term.hStderr else: term.hStdout
else: else:
import termios, posix, os, parseutils import termios, posix, os, parseutils
@ -306,7 +308,7 @@ proc setCursorPos*(f: File, x, y: int) =
let h = conHandle(f) let h = conHandle(f)
setCursorPos(h, x, y) setCursorPos(h, x, y)
else: else:
f.write(fmt"{stylePrefix}{y};{x}f") f.write(fmt"{stylePrefix}{y+1};{x+1}f")
proc setCursorXPos*(f: File, x: int) = proc setCursorXPos*(f: File, x: int) =
## Sets the terminal's cursor to the x position. ## Sets the terminal's cursor to the x position.
@ -321,7 +323,7 @@ proc setCursorXPos*(f: File, x: int) =
if setConsoleCursorPosition(h, origin) == 0: if setConsoleCursorPosition(h, origin) == 0:
raiseOSError(osLastError()) raiseOSError(osLastError())
else: else:
f.write(fmt"{stylePrefix}{x}G") f.write(fmt"{stylePrefix}{x+1}G")
when defined(windows): when defined(windows):
proc setCursorYPos*(f: File, y: int) = proc setCursorYPos*(f: File, y: int) =
@ -459,10 +461,11 @@ proc eraseScreen*(f: File) =
proc resetAttributes*(f: File) = proc resetAttributes*(f: File) =
## Resets all attributes. ## Resets all attributes.
when defined(windows): when defined(windows):
let term = getTerminal()
if f == stderr: if f == stderr:
discard setConsoleTextAttribute(hStderr, oldStderrAttr) discard setConsoleTextAttribute(term.hStderr, term.oldStderrAttr)
else: else:
discard setConsoleTextAttribute(hStdout, oldStdoutAttr) discard setConsoleTextAttribute(term.hStdout, term.oldStdoutAttr)
else: else:
f.write(ansiResetCode) f.write(ansiResetCode)
@ -471,11 +474,12 @@ type
styleBright = 1, ## bright text styleBright = 1, ## bright text
styleDim, ## dim text styleDim, ## dim text
styleItalic, ## italic (or reverse on terminals not supporting) styleItalic, ## italic (or reverse on terminals not supporting)
styleUnderscore = 4, ## underscored text styleUnderscore, ## underscored text
styleBlink, ## blinking/bold text styleBlink, ## blinking/bold text
styleReverse = 7, ## reverse styleBlinkRapid, ## rapid blinking/bold text (not widely supported)
styleHidden ## hidden text styleReverse, ## reverse
styleStrikethrough, ## strikethrough styleHidden, ## hidden text
styleStrikethrough ## strikethrough
{.deprecated: [TStyle: Style].} {.deprecated: [TStyle: Style].}
{.deprecated: [styleUnknown: styleItalic].} {.deprecated: [styleUnknown: styleItalic].}
@ -486,14 +490,7 @@ when not defined(windows):
gBG {.threadvar.}: int gBG {.threadvar.}: int
proc ansiStyleCode*(style: int): string = proc ansiStyleCode*(style: int): string =
when hasThreadSupport: result = fmt"{stylePrefix}{style}m"
result = fmt"{stylePrefix}{style}m"
else:
if styleCache.hasKey(style):
result = styleCache[style]
else:
result = fmt"{stylePrefix}{style}m"
styleCache[style] = result
template ansiStyleCode*(style: Style): string = template ansiStyleCode*(style: Style): string =
ansiStyleCode(style.int) ansiStyleCode(style.int)
@ -520,10 +517,11 @@ proc setStyle*(f: File, style: set[Style]) =
proc writeStyled*(txt: string, style: set[Style] = {styleBright}) = proc writeStyled*(txt: string, style: set[Style] = {styleBright}) =
## Writes the text `txt` in a given `style` to stdout. ## Writes the text `txt` in a given `style` to stdout.
when defined(windows): when defined(windows):
var old = getAttributes(hStdout) let term = getTerminal()
var old = getAttributes(term.hStdout)
stdout.setStyle(style) stdout.setStyle(style)
stdout.write(txt) stdout.write(txt)
discard setConsoleTextAttribute(hStdout, old) discard setConsoleTextAttribute(term.hStdout, old)
else: else:
stdout.setStyle(style) stdout.setStyle(style)
stdout.write(txt) stdout.write(txt)
@ -632,32 +630,16 @@ template ansiForegroundColorCode*(fg: static[ForegroundColor],
ansiStyleCode(fg.int + bright.int * 60) ansiStyleCode(fg.int + bright.int * 60)
proc ansiForegroundColorCode*(color: Color): string = proc ansiForegroundColorCode*(color: Color): string =
when hasThreadSupport: let rgb = extractRGB(color)
let rgb = extractRGB(color) result = fmt"{fgPrefix}{rgb.r};{rgb.g};{rgb.b}m"
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
template ansiForegroundColorCode*(color: static[Color]): string = template ansiForegroundColorCode*(color: static[Color]): string =
const rgb = extractRGB(color) const rgb = extractRGB(color)
(static(fmt"{fgPrefix}{rgb.r};{rgb.g};{rgb.b}m")) (static(fmt"{fgPrefix}{rgb.r};{rgb.g};{rgb.b}m"))
proc ansiBackgroundColorCode*(color: Color): string = proc ansiBackgroundColorCode*(color: Color): string =
when hasThreadSupport: let rgb = extractRGB(color)
let rgb = extractRGB(color) result = fmt"{bgPrefix}{rgb.r};{rgb.g};{rgb.b}m"
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
template ansiBackgroundColorCode*(color: static[Color]): string = template ansiBackgroundColorCode*(color: static[Color]): string =
const rgb = extractRGB(color) const rgb = extractRGB(color)
@ -665,16 +647,17 @@ template ansiBackgroundColorCode*(color: static[Color]): string =
proc setForegroundColor*(f: File, color: Color) = proc setForegroundColor*(f: File, color: Color) =
## Sets the terminal's foreground true color. ## Sets the terminal's foreground true color.
if trueColorIsEnabled: if getTerminal().trueColorIsEnabled:
f.write(ansiForegroundColorCode(color)) f.write(ansiForegroundColorCode(color))
proc setBackgroundColor*(f: File, color: Color) = proc setBackgroundColor*(f: File, color: Color) =
## Sets the terminal's background true color. ## Sets the terminal's background true color.
if trueColorIsEnabled: if getTerminal().trueColorIsEnabled:
f.write(ansiBackgroundColorCode(color)) f.write(ansiBackgroundColorCode(color))
proc setTrueColor(f: File, color: Color) = proc setTrueColor(f: File, color: Color) =
if fgSetColor: let term = getTerminal()
if term.fgSetColor:
setForegroundColor(f, color) setForegroundColor(f, color)
else: else:
setBackgroundColor(f, color) setBackgroundColor(f, color)
@ -726,11 +709,10 @@ macro styledWrite*(f: File, m: varargs[typed]): untyped =
## stdout.styledWrite(fgRed, "red text ") ## stdout.styledWrite(fgRed, "red text ")
## stdout.styledWrite(fgGreen, "green text") ## stdout.styledWrite(fgGreen, "green text")
## ##
let m = callsite()
var reset = false var reset = false
result = newNimNode(nnkStmtList) result = newNimNode(nnkStmtList)
for i in countup(2, m.len - 1): for i in countup(0, m.len - 1):
let item = m[i] let item = m[i]
case item.kind case item.kind
of nnkStrLit..nnkTripleStrLit: of nnkStrLit..nnkTripleStrLit:
@ -871,56 +853,65 @@ proc resetAttributes*() {.noconv.} =
proc isTrueColorSupported*(): bool = proc isTrueColorSupported*(): bool =
## Returns true if a terminal supports true color. ## Returns true if a terminal supports true color.
return trueColorIsSupported return getTerminal().trueColorIsSupported
when defined(windows): when defined(windows):
import os import os
proc enableTrueColors*() = proc enableTrueColors*() =
## Enable true color. ## Enable true color.
var term = getTerminal()
when defined(windows): when defined(windows):
var var
ver: OSVERSIONINFO ver: OSVERSIONINFO
ver.dwOSVersionInfoSize = sizeof(ver).DWORD ver.dwOSVersionInfoSize = sizeof(ver).DWORD
let res = getVersionExW(addr ver) let res = getVersionExW(addr ver)
if res == 0: if res == 0:
trueColorIsSupported = false term.trueColorIsSupported = false
else: else:
trueColorIsSupported = ver.dwMajorVersion > 10 or term.trueColorIsSupported = ver.dwMajorVersion > 10 or
(ver.dwMajorVersion == 10 and (ver.dwMinorVersion > 0 or (ver.dwMajorVersion == 10 and (ver.dwMinorVersion > 0 or
(ver.dwMinorVersion == 0 and ver.dwBuildNumber >= 10586))) (ver.dwMinorVersion == 0 and ver.dwBuildNumber >= 10586)))
if not trueColorIsSupported: if not term.trueColorIsSupported:
trueColorIsSupported = getEnv("ANSICON_DEF").len > 0 term.trueColorIsSupported = getEnv("ANSICON_DEF").len > 0
if trueColorIsSupported: if term.trueColorIsSupported:
if getEnv("ANSICON_DEF").len == 0: if getEnv("ANSICON_DEF").len == 0:
var mode: DWORD = 0 var mode: DWORD = 0
if getConsoleMode(getStdHandle(STD_OUTPUT_HANDLE), addr(mode)) != 0: if getConsoleMode(getStdHandle(STD_OUTPUT_HANDLE), addr(mode)) != 0:
mode = mode or ENABLE_VIRTUAL_TERMINAL_PROCESSING mode = mode or ENABLE_VIRTUAL_TERMINAL_PROCESSING
if setConsoleMode(getStdHandle(STD_OUTPUT_HANDLE), mode) != 0: if setConsoleMode(getStdHandle(STD_OUTPUT_HANDLE), mode) != 0:
trueColorIsEnabled = true term.trueColorIsEnabled = true
else: else:
trueColorIsEnabled = false term.trueColorIsEnabled = false
else: else:
trueColorIsEnabled = true term.trueColorIsEnabled = true
else: else:
trueColorIsSupported = string(getEnv("COLORTERM")).toLowerAscii() in ["truecolor", "24bit"] term.trueColorIsSupported = string(getEnv("COLORTERM")).toLowerAscii() in ["truecolor", "24bit"]
trueColorIsEnabled = trueColorIsSupported term.trueColorIsEnabled = term.trueColorIsSupported
proc disableTrueColors*() = proc disableTrueColors*() =
## Disable true color. ## Disable true color.
var term = getTerminal()
when defined(windows): when defined(windows):
if trueColorIsSupported: if term.trueColorIsSupported:
if getEnv("ANSICON_DEF").len == 0: if getEnv("ANSICON_DEF").len == 0:
var mode: DWORD = 0 var mode: DWORD = 0
if getConsoleMode(getStdHandle(STD_OUTPUT_HANDLE), addr(mode)) != 0: if getConsoleMode(getStdHandle(STD_OUTPUT_HANDLE), addr(mode)) != 0:
mode = mode and not ENABLE_VIRTUAL_TERMINAL_PROCESSING mode = mode and not ENABLE_VIRTUAL_TERMINAL_PROCESSING
discard setConsoleMode(getStdHandle(STD_OUTPUT_HANDLE), mode) discard setConsoleMode(getStdHandle(STD_OUTPUT_HANDLE), mode)
trueColorIsEnabled = false term.trueColorIsEnabled = false
else: else:
trueColorIsEnabled = false term.trueColorIsEnabled = false
proc newTerminal(): PTerminal =
new result
when defined(windows):
initTerminal(result)
when not defined(testing) and isMainModule: when not defined(testing) and isMainModule:
assert ansiStyleCode(styleBright) == "\e[1m"
assert ansiStyleCode(styleStrikethrough) == "\e[9m"
#system.addQuitProc(resetAttributes) #system.addQuitProc(resetAttributes)
write(stdout, "never mind") write(stdout, "never mind")
stdout.eraseLine() 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(2) #=> 9223372036854775807
## high(int) #=> 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*[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: array[I, T]): I {.magic: "High", noSideEffect.}
proc high*[I, T](x: typeDesc[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: cstring): int {.magic: "High", noSideEffect.}
proc high*(x: string): 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*[T](x: openArray[T]): int {.magic: "Low", noSideEffect.}
proc low*[I, T](x: array[I, T]): I {.magic: "Low", noSideEffect.} proc low*[I, T](x: array[I, T]): I {.magic: "Low", noSideEffect.}
proc low*[T](x: T): T {.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} ## var i = {1,2,3,4}
## card(i) #=> 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``. ## returns the internal int value of an ordinal value ``x``.
## ##
## .. code-block:: nim ## .. code-block:: nim
@ -2959,6 +2959,22 @@ when not defined(JS): #and not defined(nimscript):
## useful for low-level file access ## useful for low-level file access
include "system/ansi_c" 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 = proc cmp(x, y: string): int =
when nimvm: when nimvm:
@ -2967,7 +2983,7 @@ when not defined(JS): #and not defined(nimscript):
else: result = 0 else: result = 0
else: else:
let minlen = min(x.len, y.len) 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: if result == 0:
result = x.len - y.len result = x.len - y.len
@ -3256,22 +3272,6 @@ when not defined(JS): #and not defined(nimscript):
when defined(memtracker): when defined(memtracker):
include "system/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": when hostOS == "standalone":
include "system/embedded" include "system/embedded"
else: else:
@ -4154,11 +4154,10 @@ template doAssertRaises*(exception, code: untyped): typed =
## .. code-block:: nim ## .. code-block:: nim
## doAssertRaises(ValueError): ## doAssertRaises(ValueError):
## raise newException(ValueError, "Hello World") ## raise newException(ValueError, "Hello World")
# TODO: investigate why runnableExamples here caused
# https://github.com/nim-lang/Nim/issues/8223
var wrong = false var wrong = false
try: try:
code if true:
code
wrong = true wrong = true
except exception: except exception:
discard discard

View file

@ -830,7 +830,7 @@ proc rawDealloc(a: var MemRegion, p: pointer) =
c.freeList = f c.freeList = f
when overwriteFree: when overwriteFree:
# set to 0xff to check for usage after free bugs: # 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)) s -% sizeof(FreeCell))
# check if it is not in the freeSmallChunks[s] list: # check if it is not in the freeSmallChunks[s] list:
if c.free < s: if c.free < s:
@ -847,7 +847,7 @@ proc rawDealloc(a: var MemRegion, p: pointer) =
s == 0, "rawDealloc 2") s == 0, "rawDealloc 2")
else: else:
# set to 0xff to check for usage after free bugs: # 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 # free big chunk
var c = cast[PBigChunk](c) var c = cast[PBigChunk](c)
dec a.occ, c.size 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.} importc: "memset", header: "<string.h>", discardable.}
proc c_strcmp(a, b: cstring): cint {. proc c_strcmp(a, b: cstring): cint {.
importc: "strcmp", header: "<string.h>", noSideEffect.} importc: "strcmp", header: "<string.h>", noSideEffect.}
proc c_strlen(a: cstring): csize {.
importc: "strlen", header: "<string.h>", noSideEffect.}
when defined(linux) and defined(amd64): when defined(linux) and defined(amd64):
type 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: while true:
# memset to \L so that we can tell how far fgets wrote, even on EOF, where # memset to \L so that we can tell how far fgets wrote, even on EOF, where
# fgets doesn't append an \L # 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 var fgetsSuccess = c_fgets(addr line.string[pos], sp, f) != nil
if not fgetsSuccess: checkErr(f) if not fgetsSuccess: checkErr(f)
let m = c_memchr(addr line.string[pos], '\L'.ord, sp) 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_new*(context: SslCtx): SslPtr{.cdecl, dynlib: DLLSSLName, importc.}
proc SSL_free*(ssl: 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_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, proc SSL_CTX_new*(meth: PSSL_METHOD): SslCtx{.cdecl,
dynlib: DLLSSLName, importc.} dynlib: DLLSSLName, importc.}
proc SSL_CTX_load_verify_locations*(ctx: SslCtx, CAfile: cstring, 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: block timezoneTests:
let dt = initDateTime(01, mJan, 2017, 12, 00, 00, utcPlus2) let dt = initDateTime(01, mJan, 2017, 12, 00, 00, utcPlus2)
doAssert $dt == "2017-01-01T12:00:00+02:00" 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 $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, 1911, 12, 00, 00, utc()) == "1911-01-01T12:00:00Z"
doAssert $initDateTime(01, mJan, 0023, 12, 00, 00, utc()) == "0023-01-01T12:00:00+00:00" 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)" else: echo "Does not compute! (test OK)"
foo() 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 = iterator tokenize(format: string): char =
var i = 0 var i = 0
while true: while i < format.len:
case format[i] case format[i]
of '%': of '%':
case format[i+1] case format[i+1]
@ -42,7 +42,8 @@ macro printf(formatString: string{lit}, args: varargs[typed]): untyped =
$expectedType & ", actual type: " & $actualType $expectedType & ", actual type: " & $actualType
# keep the original callsite, but use cprintf instead # keep the original callsite, but use cprintf instead
result = callsite() result = newCall(bindSym"printfImpl")
result[0] = bindSym"printfImpl" result.add formatString
for a in args: result.add a
printf("test %d\n", 10) printf("test %d\n", 10)

View file

@ -5,4 +5,7 @@ discard """
# Tests that module names can contain multi byte characters # Tests that module names can contain multi byte characters
let a = 1 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 type Annotated {.simpleAttr.} = object
proc generic_proc[T]() = 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 import
times, os, strutils, unittest 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' # $ date --date='@2147483647'
# Tue 19 Jan 03:14:07 GMT 2038 # Tue 19 Jan 03:14:07 GMT 2038
@ -19,25 +37,10 @@ proc checkFormat(t: DateTime, format, expected: string) =
echo "actual : ", actual echo "actual : ", actual
doAssert false doAssert false
let t = fromUnix(2147483647).utc let t2 = fromUnix(160070789).utc() # Mon 27 Jan 16:06:29 GMT 1975
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
t2.checkFormat("d dd ddd dddd h hh H HH m mm M MM MMM MMMM s" & 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", " 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 var t4 = fromUnix(876124714).utc # Mon 6 Oct 08:58:34 BST 1997
t4.checkFormat("M MM MMM MMMM", "10 10 Oct October") t4.checkFormat("M MM MMM MMMM", "10 10 Oct October")
@ -83,16 +86,16 @@ let seqB: seq[Time] = @[]
doAssert seqA == seqB doAssert seqA == seqB
for tz in [ for tz in [
(0, "+0", "+00", "+00:00"), # UTC (staticTz(seconds = 0), "+0", "+00", "+00:00"), # UTC
(-3600, "+1", "+01", "+01:00"), # CET (staticTz(seconds = -3600), "+1", "+01", "+01:00"), # CET
(-39600, "+11", "+11", "+11:00"), # two digits (staticTz(seconds = -39600), "+11", "+11", "+11:00"), # two digits
(-1800, "+0", "+00", "+00:30"), # half an hour (staticTz(seconds = -1800), "+0", "+00", "+00:30"), # half an hour
(7200, "-2", "-02", "-02:00"), # positive (staticTz(seconds = 7200), "-2", "-02", "-02:00"), # positive
(38700, "-10", "-10", "-10:45")]: # positive with three quaters hour (staticTz(seconds = 38700), "-10", "-10", "-10:45")]: # positive with three quaters hour
let ti = DateTime(month: mJan, monthday: 1, utcOffset: tz[0]) let dt = initDateTime(1, mJan, 2000, 00, 00, 00, tz[0])
doAssert ti.format("z") == tz[1] doAssert dt.format("z") == tz[1]
doAssert ti.format("zz") == tz[2] doAssert dt.format("zz") == tz[2]
doAssert ti.format("zzz") == tz[3] doAssert dt.format("zzz") == tz[3]
block countLeapYears: block countLeapYears:
# 1920, 2004 and 2020 are leap years, and should be counted starting at the following year # 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 let
parsed = s.parse(f, utc()) parsed = s.parse(f, utc())
parsedStr = $parsed parsedStr = $parsed
if parsedStr != sExpected:
echo "GOT ", parsedStr, " EXPECTED ", sExpected, " FORMAT ", f
check parsedStr == sExpected check parsedStr == sExpected
if parsed.yearday != ydExpected:
echo s
echo parsed.repr
echo parsed.yearday, " exp: ", ydExpected
check(parsed.yearday == ydExpected) check(parsed.yearday == ydExpected)
template parseTestExcp(s, f: string) = template parseTestExcp(s, f: string) =
@ -130,51 +131,43 @@ template parseTestTimeOnly(s, f, sExpected: string) =
# explicit timezone offsets in all tests. # explicit timezone offsets in all tests.
template runTimezoneTests() = template runTimezoneTests() =
parseTest("Tuesday at 09:04am on Dec 15, 2015 +0", 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" # 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", 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" # 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", 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" # 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", 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" # RFC822 = "02 Jan 06 15:04 MST"
parseTest("12 Jan 16 15:04 +0", "dd MMM yy HH:mm z", 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 # 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", 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" # 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", 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" # 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", 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 # 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", 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" # 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", 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" # RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00"
parseTest("2006-01-12T15:04:05.999999999Z-07: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"]: for tzFormat in ["z", "zz", "zzz"]:
# formatting timezone as 'Z' for UTC # formatting timezone as 'Z' for UTC
parseTest("2001-01-12T22:04:05Z", "yyyy-MM-dd'T'HH:mm:ss" & tzFormat, 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" # Kitchen = "3:04PM"
parseTestTimeOnly("3:04PM", "h:mmtt", "15:04:00") 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 # 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 # the date being parsed. Need to test parse dates both in and out of DST. We
@ -195,8 +188,8 @@ template runTimezoneTests() =
let let
parsedJan = parse("2016-01-05 04:00:00+01:00", "yyyy-MM-dd HH:mm:sszzz") 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") parsedJul = parse("2016-07-01 04:00:00+01:00", "yyyy-MM-dd HH:mm:sszzz")
doAssert toTime(parsedJan).toUnix == 1451962800 check toTime(parsedJan).toUnix == 1451962800
doAssert toTime(parsedJul).toUnix == 1467342000 check toTime(parsedJul).toUnix == 1467342000
suite "ttimes": suite "ttimes":
@ -256,7 +249,7 @@ suite "ttimes":
check $(dt + initDuration(days = 1)) == "2017-03-26T13:00:00+02:00" check $(dt + initDuration(days = 1)) == "2017-03-26T13:00:00+02:00"
test "datetime before epoch": 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": test "adding/subtracting time across dst":
putenv("TZ", "Europe/Stockholm") putenv("TZ", "Europe/Stockholm")
@ -319,6 +312,15 @@ suite "ttimes":
test "incorrect inputs: timezone (zzz) 3": test "incorrect inputs: timezone (zzz) 3":
parseTestExcp("2018-02-19 16:30:00 +01:0", "yyyy-MM-dd hh:mm:ss zzz") 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": test "dynamic timezone":
proc staticOffset(offset: int): Timezone = proc staticOffset(offset: int): Timezone =
proc zoneInfoFromTz(adjTime: Time): ZonedTime = proc zoneInfoFromTz(adjTime: Time): ZonedTime =
@ -340,7 +342,7 @@ suite "ttimes":
check dt.utcOffset == -9000 check dt.utcOffset == -9000
check dt.isDst == false check dt.isDst == false
check $dt == "2000-01-01T12:00:00+02:30" 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 check $dt.utc.inZone(tz) == $dt
test "isLeapYear": test "isLeapYear":
@ -351,12 +353,12 @@ suite "ttimes":
test "subtract months": test "subtract months":
var dt = initDateTime(1, mFeb, 2017, 00, 00, 00, utc()) 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()) 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()) dt = initDateTime(31, mMar, 2017, 00, 00, 00, utc())
# This happens due to monthday overflow. It's consistent with Phobos. # 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": test "duration":
let d = initDuration 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)
discard initDateTime(1, mJan, 35_000, 12, 00, 00) discard initDateTime(1, mJan, 35_000, 12, 00, 00)
# with duration/timeinterval # 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) initDuration(seconds = 1)
check dt.second == 1 check dt.second == 1
let dt2 = dt + 35_001.years 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": test "compare datetimes":
var dt1 = now() var dt1 = now()
@ -426,4 +428,90 @@ suite "ttimes":
check (-1).fromWinTime.nanosecond == convert(Seconds, Nanoseconds, 1) - 100 check (-1).fromWinTime.nanosecond == convert(Seconds, Nanoseconds, 1) - 100
check -1.fromWinTime.toWinTime == -1 check -1.fromWinTime.toWinTime == -1
# One nanosecond is discarded due to differences in time resolution # 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 """ discard """
output:"" output: ""
""" """
doAssert "@[23, 45]" == $(@[23, 45]) doAssert "@[23, 45]" == $(@[23, 45])
doAssert "[32, 45]" == $([32, 45]) doAssert "[32, 45]" == $([32, 45])
doAssert """@["", "foo", "bar"]""" == $(@["", "foo", "bar"]) doAssert """@["", "foo", "bar"]""" == $(@["", "foo", "bar"])
doAssert """["", "foo", "bar"]""" == $(["", "foo", "bar"]) doAssert """["", "foo", "bar"]""" == $(["", "foo", "bar"])
doAssert """["", "foo", "bar"]""" == $(@["", "foo", "bar"].toOpenArray(0, 2)) doAssert """["", "foo", "bar"]""" == $(@["", "foo", "bar"].toOpenArray(0, 2))
# bug #2395 # bug #2395
let alphaSet: set[char] = {'a'..'c'} let alphaSet: set[char] = {'a'..'c'}
@ -69,13 +69,13 @@ var yy: string
doAssert xx == @[] doAssert xx == @[]
doAssert yy == "" doAssert yy == ""
proc bar(arg: cstring): void = proc bar(arg: cstring) =
doAssert arg[0] == '\0' doAssert arg[0] == '\0'
proc baz(arg: openarray[char]): void = proc baz(arg: openarray[char]) =
doAssert arg.len == 0 doAssert arg.len == 0
proc stringCompare(): void = proc stringCompare() =
var a,b,c,d,e,f,g: string var a,b,c,d,e,f,g: string
a.add 'a' a.add 'a'
doAssert a == "a" doAssert a == "a"
@ -102,9 +102,12 @@ proc stringCompare(): void =
doAssert "" != "\0\0\0\0\0\0\0\0\0\0" doAssert "" != "\0\0\0\0\0\0\0\0\0\0"
var nilstring: string var nilstring: string
bar(nilstring) #bar(nilstring)
baz(nilstring) baz(nilstring)
stringCompare() stringCompare()
var nilstring: string
bar(nilstring)
static: static:
stringCompare() 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')])