fix merge conflict
This commit is contained in:
commit
4389409e26
65 changed files with 2187 additions and 917 deletions
18
bin/nim-gdb
Executable file
18
bin/nim-gdb
Executable 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" \
|
||||||
|
"$@"
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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!
|
||||||
|
|
|
||||||
|
|
@ -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):
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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, ")")
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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]
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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])
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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``.
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
12
doc/nimc.rst
12
doc/nimc.rst
|
|
@ -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:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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]
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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")
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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]")
|
||||||
|
|
|
||||||
|
|
@ -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`,
|
||||||
|
|
|
||||||
|
|
@ -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):
|
||||||
|
|
|
||||||
|
|
@ -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}"
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
1370
lib/pure/times.nim
1370
lib/pure/times.nim
File diff suppressed because it is too large
Load diff
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
47
lib/system/memory.nim
Normal 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
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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
10
tests/casestmt/t8333.nim
Normal 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
16
tests/concepts/t8280.nim
Normal 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
31
tests/converter/t7098.nim
Normal 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
15
tests/generics/t7794.nim
Normal 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
7
tests/generics/t8270.nim
Normal 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
21
tests/init/t8314.nim
Normal 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)
|
||||||
|
|
@ -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"
|
||||||
|
|
@ -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)
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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")
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
33
tests/untestable/gdb/gdb_pretty_printer_test.py
Normal file
33
tests/untestable/gdb/gdb_pretty_printer_test.py
Normal 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")
|
||||||
3
tests/untestable/gdb/gdb_pretty_printer_test_output.txt
Normal file
3
tests/untestable/gdb/gdb_pretty_printer_test_output.txt
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
Loading Nim Runtime support.
|
||||||
|
NimEnumPrinter: lookup global symbol 'NTI_z9cu80OJCfNgw9bUdzn5ZEzw_ failed for tyEnum_MyOtherEnum_z9cu80OJCfNgw9bUdzn5ZEzw.
|
||||||
|
8
|
||||||
53
tests/untestable/gdb/gdb_pretty_printer_test_program.nim
Normal file
53
tests/untestable/gdb/gdb_pretty_printer_test_program.nim
Normal 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()
|
||||||
15
tests/untestable/gdb/gdb_pretty_printer_test_run.sh
Executable file
15
tests/untestable/gdb/gdb_pretty_printer_test_run.sh
Executable 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
513
tools/nim-gdb.py
Normal 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')])
|
||||||
Loading…
Add table
Add a link
Reference in a new issue