mirror of
https://github.com/nim-lang/Nim.git
synced 2026-02-19 01:18:32 +00:00
fix merge conflict
This commit is contained in:
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
|
||||
require a second mandatory parameter ``skipNonAlpha``.
|
||||
|
||||
- ``osLastError`` is now marked with ``sideEffect``
|
||||
- The procs ``parseHexInt`` and ``parseOctInt`` now fail on empty strings
|
||||
and strings containing only valid prefixes, e.g. "0x" for hex integers.
|
||||
|
||||
- ``terminal.setCursorPos`` and ``terminal.setCursorXPos`` now work correctly
|
||||
with 0-based coordinates on POSIX (previously, you needed to use
|
||||
1-based coordinates on POSIX for correct behaviour; the Windows behaviour
|
||||
was always correct).
|
||||
|
||||
|
||||
#### Breaking changes in the compiler
|
||||
|
||||
|
||||
@@ -322,7 +322,8 @@ const
|
||||
usesEffects* = 1 # read effects at position 1
|
||||
writeEffects* = 2 # write effects at position 2
|
||||
tagEffects* = 3 # user defined tag ('gc', 'time' etc.)
|
||||
effectListLen* = 4 # list of effects list
|
||||
pragmasEffects* = 4 # not an effect, but a slot for pragmas in proc type
|
||||
effectListLen* = 5 # list of effects list
|
||||
|
||||
type
|
||||
TTypeKind* = enum # order is important!
|
||||
|
||||
@@ -27,9 +27,9 @@ proc lineInfoToStr*(conf: ConfigRef; info: TLineInfo): Rope
|
||||
when declared(echo):
|
||||
# these are for debugging only: They are not really deprecated, but I want
|
||||
# the warning so that release versions do not contain debugging statements:
|
||||
proc debug*(n: PSym; conf: ConfigRef = nil) {.deprecated.}
|
||||
proc debug*(n: PType; conf: ConfigRef = nil) {.deprecated.}
|
||||
proc debug*(n: PNode; conf: ConfigRef = nil) {.deprecated.}
|
||||
proc debug*(n: PSym; conf: ConfigRef = nil) {.exportc: "debugSym", deprecated.}
|
||||
proc debug*(n: PType; conf: ConfigRef = nil) {.exportc: "debugType", deprecated.}
|
||||
proc debug*(n: PNode; conf: ConfigRef = nil) {.exportc: "debugNode", deprecated.}
|
||||
|
||||
template debug*(x: PSym|PType|PNode) {.deprecated.} =
|
||||
when compiles(c.config):
|
||||
|
||||
@@ -255,16 +255,14 @@ proc genGenericAsgn(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
|
||||
# here for this flag, where it is reasonably safe to do so
|
||||
# (for objects, etc.):
|
||||
if p.config.selectedGC == gcDestructors:
|
||||
useStringh(p.module)
|
||||
linefmt(p, cpsStmts,
|
||||
"memcpy((void*)$1, (NIM_CONST void*)$2, sizeof($3));$n",
|
||||
addrLoc(p.config, dest), addrLoc(p.config, src), rdLoc(dest))
|
||||
"#nimCopyMem((void*)$1, (NIM_CONST void*)$2, sizeof($3));$n",
|
||||
addrLoc(p.config, dest), addrLoc(p.config, src), rdLoc(dest))
|
||||
elif needToCopy notin flags or
|
||||
tfShallow in skipTypes(dest.t, abstractVarRange).flags:
|
||||
if dest.storage == OnStack or not usesWriteBarrier(p.config):
|
||||
useStringh(p.module)
|
||||
linefmt(p, cpsStmts,
|
||||
"memcpy((void*)$1, (NIM_CONST void*)$2, sizeof($3));$n",
|
||||
"#nimCopyMem((void*)$1, (NIM_CONST void*)$2, sizeof($3));$n",
|
||||
addrLoc(p.config, dest), addrLoc(p.config, src), rdLoc(dest))
|
||||
else:
|
||||
linefmt(p, cpsStmts, "#genericShallowAssign((void*)$1, (void*)$2, $3);$n",
|
||||
@@ -345,9 +343,8 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
|
||||
if needsComplexAssignment(dest.t):
|
||||
genGenericAsgn(p, dest, src, flags)
|
||||
else:
|
||||
useStringh(p.module)
|
||||
linefmt(p, cpsStmts,
|
||||
"memcpy((void*)$1, (NIM_CONST void*)$2, sizeof($3));$n",
|
||||
"#nimCopyMem((void*)$1, (NIM_CONST void*)$2, sizeof($3));$n",
|
||||
rdLoc(dest), rdLoc(src), getTypeDesc(p.module, dest.t))
|
||||
of tyOpenArray, tyVarargs:
|
||||
# open arrays are always on the stack - really? What if a sequence is
|
||||
@@ -358,16 +355,14 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
|
||||
addrLoc(p.config, dest), addrLoc(p.config, src),
|
||||
genTypeInfo(p.module, dest.t, dest.lode.info))
|
||||
else:
|
||||
useStringh(p.module)
|
||||
linefmt(p, cpsStmts,
|
||||
# bug #4799, keep the memcpy for a while
|
||||
#"memcpy((void*)$1, (NIM_CONST void*)$2, sizeof($1[0])*$1Len_0);$n",
|
||||
# bug #4799, keep the nimCopyMem for a while
|
||||
#"#nimCopyMem((void*)$1, (NIM_CONST void*)$2, sizeof($1[0])*$1Len_0);$n",
|
||||
"$1 = $2;$n",
|
||||
rdLoc(dest), rdLoc(src))
|
||||
of tySet:
|
||||
if mapType(p.config, ty) == ctArray:
|
||||
useStringh(p.module)
|
||||
linefmt(p, cpsStmts, "memcpy((void*)$1, (NIM_CONST void*)$2, $3);$n",
|
||||
linefmt(p, cpsStmts, "#nimCopyMem((void*)$1, (NIM_CONST void*)$2, $3);$n",
|
||||
rdLoc(dest), rdLoc(src), rope(getSize(p.config, dest.t)))
|
||||
else:
|
||||
linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src))
|
||||
@@ -412,8 +407,7 @@ proc genDeepCopy(p: BProc; dest, src: TLoc) =
|
||||
genTypeInfo(p.module, dest.t, dest.lode.info))
|
||||
of tySet:
|
||||
if mapType(p.config, ty) == ctArray:
|
||||
useStringh(p.module)
|
||||
linefmt(p, cpsStmts, "memcpy((void*)$1, (NIM_CONST void*)$2, $3);$n",
|
||||
linefmt(p, cpsStmts, "#nimCopyMem((void*)$1, (NIM_CONST void*)$2, $3);$n",
|
||||
rdLoc(dest), rdLoc(src), rope(getSize(p.config, dest.t)))
|
||||
else:
|
||||
linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src))
|
||||
@@ -1515,9 +1509,8 @@ proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||
if op == mHigh: unaryExpr(p, e, d, "($1Len_0-1)")
|
||||
else: unaryExpr(p, e, d, "$1Len_0")
|
||||
of tyCString:
|
||||
useStringh(p.module)
|
||||
if op == mHigh: unaryExpr(p, e, d, "($1 ? (strlen($1)-1) : -1)")
|
||||
else: unaryExpr(p, e, d, "($1 ? strlen($1) : 0)")
|
||||
if op == mHigh: unaryExpr(p, e, d, "($1 ? (#nimCStrLen($1)-1) : -1)")
|
||||
else: unaryExpr(p, e, d, "($1 ? #nimCStrLen($1) : 0)")
|
||||
of tyString:
|
||||
if not p.module.compileToCpp:
|
||||
if op == mHigh: unaryExpr(p, e, d, "($1 ? ($1->Sup.len-1) : -1)")
|
||||
@@ -1672,7 +1665,7 @@ proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||
" $3 = (($4[$1] & ~ $5[$1]) == 0);$n" &
|
||||
" if (!$3) break;}$n", "for ($1 = 0; $1 < $2; $1++) { $n" &
|
||||
" $3 = (($4[$1] & ~ $5[$1]) == 0);$n" & " if (!$3) break;}$n" &
|
||||
"if ($3) $3 = (memcmp($4, $5, $2) != 0);$n",
|
||||
"if ($3) $3 = (#nimCmpMem($4, $5, $2) != 0);$n",
|
||||
"&", "|", "& ~", "^"]
|
||||
var a, b, i: TLoc
|
||||
var setType = skipTypes(e.sons[1].typ, abstractVar)
|
||||
@@ -1711,11 +1704,10 @@ proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||
initLocExpr(p, e.sons[1], a)
|
||||
initLocExpr(p, e.sons[2], b)
|
||||
if d.k == locNone: getTemp(p, getSysType(p.module.g.graph, unknownLineInfo(), tyBool), d)
|
||||
lineF(p, cpsStmts, lookupOpr[op],
|
||||
linefmt(p, cpsStmts, lookupOpr[op],
|
||||
[rdLoc(i), rope(size), rdLoc(d), rdLoc(a), rdLoc(b)])
|
||||
of mEqSet:
|
||||
useStringh(p.module)
|
||||
binaryExprChar(p, e, d, "(memcmp($1, $2, " & $(size) & ")==0)")
|
||||
binaryExprChar(p, e, d, "(#nimCmpMem($1, $2, " & $(size) & ")==0)")
|
||||
of mMulSet, mPlusSet, mMinusSet, mSymDiffSet:
|
||||
# we inline the simple for loop for better code generation:
|
||||
getTemp(p, getSysType(p.module.g.graph, unknownLineInfo(), tyInt), i) # our counter
|
||||
@@ -1979,7 +1971,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||
proc genSetConstr(p: BProc, e: PNode, d: var TLoc) =
|
||||
# example: { a..b, c, d, e, f..g }
|
||||
# we have to emit an expression of the form:
|
||||
# memset(tmp, 0, sizeof(tmp)); inclRange(tmp, a, b); incl(tmp, c);
|
||||
# nimZeroMem(tmp, sizeof(tmp)); inclRange(tmp, a, b); incl(tmp, c);
|
||||
# incl(tmp, d); incl(tmp, e); inclRange(tmp, f, g);
|
||||
var
|
||||
a, b, idx: TLoc
|
||||
@@ -1989,8 +1981,7 @@ proc genSetConstr(p: BProc, e: PNode, d: var TLoc) =
|
||||
if d.k == locNone: getTemp(p, e.typ, d)
|
||||
if getSize(p.config, e.typ) > 8:
|
||||
# big set:
|
||||
useStringh(p.module)
|
||||
lineF(p, cpsStmts, "memset($1, 0, sizeof($2));$n",
|
||||
linefmt(p, cpsStmts, "#nimZeroMem($1, sizeof($2));$n",
|
||||
[rdLoc(d), getTypeDesc(p.module, e.typ)])
|
||||
for it in e.sons:
|
||||
if it.kind == nkRange:
|
||||
|
||||
@@ -1146,5 +1146,9 @@ proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) =
|
||||
|
||||
proc genStmts(p: BProc, t: PNode) =
|
||||
var a: TLoc
|
||||
|
||||
let isPush = hintExtendedContext in p.config.notes
|
||||
if isPush: pushInfoContext(p.config, t.info)
|
||||
expr(p, t, a)
|
||||
if isPush: popInfoContext(p.config)
|
||||
internalAssert p.config, a.k in {locNone, locTemp, locLocalVar}
|
||||
|
||||
@@ -48,7 +48,7 @@ proc mangleName(m: BModule; s: PSym): Rope =
|
||||
result = s.loc.r
|
||||
if result == nil:
|
||||
result = s.name.s.mangle.rope
|
||||
add(result, idOrSig(s, m.module.name.s, m.sigConflicts))
|
||||
add(result, idOrSig(s, m.module.name.s.mangle, m.sigConflicts))
|
||||
s.loc.r = result
|
||||
writeMangledName(m.ndi, s, m.config)
|
||||
|
||||
|
||||
@@ -80,11 +80,6 @@ proc isSimpleConst(typ: PType): bool =
|
||||
{tyTuple, tyObject, tyArray, tySet, tySequence} and not
|
||||
(t.kind == tyProc and t.callConv == ccClosure)
|
||||
|
||||
proc useStringh(m: BModule) =
|
||||
if includesStringh notin m.flags:
|
||||
incl m.flags, includesStringh
|
||||
m.includeHeader("<string.h>")
|
||||
|
||||
proc useHeader(m: BModule, sym: PSym) =
|
||||
if lfHeader in sym.loc.flags:
|
||||
assert(sym.annex != nil)
|
||||
@@ -329,10 +324,9 @@ proc resetLoc(p: BProc, loc: var TLoc) =
|
||||
# field, so disabling this should be safe:
|
||||
genObjectInit(p, cpsStmts, loc.t, loc, true)
|
||||
else:
|
||||
useStringh(p.module)
|
||||
# array passed as argument decayed into pointer, bug #7332
|
||||
# so we use getTypeDesc here rather than rdLoc(loc)
|
||||
linefmt(p, cpsStmts, "memset((void*)$1, 0, sizeof($2));$n",
|
||||
linefmt(p, cpsStmts, "#nimZeroMem((void*)$1, sizeof($2));$n",
|
||||
addrLoc(p.config, loc), getTypeDesc(p.module, loc.t))
|
||||
# XXX: We can be extra clever here and call memset only
|
||||
# on the bytes following the m_type field?
|
||||
@@ -345,11 +339,10 @@ proc constructLoc(p: BProc, loc: TLoc, isTemp = false) =
|
||||
getTypeDesc(p.module, typ))
|
||||
else:
|
||||
if not isTemp or containsGarbageCollectedRef(loc.t):
|
||||
# don't use memset for temporary values for performance if we can
|
||||
# don't use nimZeroMem for temporary values for performance if we can
|
||||
# avoid it:
|
||||
if not isImportedCppType(typ):
|
||||
useStringh(p.module)
|
||||
linefmt(p, cpsStmts, "memset((void*)$1, 0, sizeof($2));$n",
|
||||
linefmt(p, cpsStmts, "#nimZeroMem((void*)$1, sizeof($2));$n",
|
||||
addrLoc(p.config, loc), getTypeDesc(p.module, typ))
|
||||
genObjectInit(p, cpsStmts, loc.t, loc, true)
|
||||
|
||||
@@ -680,6 +673,7 @@ proc generateHeaders(m: BModule) =
|
||||
add(m.s[cfsHeaders], "#undef linux\L")
|
||||
add(m.s[cfsHeaders], "#undef mips\L")
|
||||
add(m.s[cfsHeaders], "#undef near\L")
|
||||
add(m.s[cfsHeaders], "#undef far\L")
|
||||
add(m.s[cfsHeaders], "#undef powerpc\L")
|
||||
add(m.s[cfsHeaders], "#undef unix\L")
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ import
|
||||
wordrecg, syntaxes, renderer, lexer, packages/docutils/rstast,
|
||||
packages/docutils/rst, packages/docutils/rstgen, times,
|
||||
packages/docutils/highlite, sempass2, json, xmltree, cgi,
|
||||
typesrenderer, astalgo, modulepaths, lineinfos
|
||||
typesrenderer, astalgo, modulepaths, lineinfos, sequtils
|
||||
|
||||
type
|
||||
TSections = array[TSymKind, Rope]
|
||||
@@ -259,11 +259,19 @@ proc nodeToHighlightedHtml(d: PDoc; n: PNode; result: var Rope; renderFlags: TRe
|
||||
of tkSpaces, tkInvalid:
|
||||
add(result, literal)
|
||||
of tkCurlyDotLe:
|
||||
dispA(d.conf, result, """<span class="Other pragmabegin">$1</span><div class="pragma">""",
|
||||
dispA(d.conf, result, "<span>" & # This span is required for the JS to work properly
|
||||
"""<span class="Other">{</span><span class="Other pragmadots">...</span><span class="Other">}</span>
|
||||
</span>
|
||||
<span class="pragmawrap">
|
||||
<span class="Other">$1</span>
|
||||
<span class="pragma">""".replace("\n", ""), # Must remove newlines because wrapped in a <pre>
|
||||
"\\spanOther{$1}",
|
||||
[rope(esc(d.target, literal))])
|
||||
of tkCurlyDotRi:
|
||||
dispA(d.conf, result, "</div><span class=\"Other pragmaend\">$1</span>",
|
||||
dispA(d.conf, result, """
|
||||
</span>
|
||||
<span class="Other">$1</span>
|
||||
</span>""".replace("\n", ""),
|
||||
"\\spanOther{$1}",
|
||||
[rope(esc(d.target, literal))])
|
||||
of tkParLe, tkParRi, tkBracketLe, tkBracketRi, tkCurlyLe, tkCurlyRi,
|
||||
@@ -280,7 +288,7 @@ proc getAllRunnableExamples(d: PDoc; n: PNode; dest: var Rope) =
|
||||
of nkCallKinds:
|
||||
if n[0].kind == nkSym and n[0].sym.magic == mRunnableExamples and
|
||||
n.len >= 2 and n.lastSon.kind == nkStmtList:
|
||||
dispA(d.conf, dest, "\n<strong class=\"examples_text\">$1</strong>\n",
|
||||
dispA(d.conf, dest, "\n<p><strong class=\"examples_text\">$1</strong></p>\n",
|
||||
"\n\\textbf{$1}\n", [rope"Examples:"])
|
||||
inc d.listingCounter
|
||||
let id = $d.listingCounter
|
||||
|
||||
@@ -434,10 +434,7 @@ proc completeCFilePath*(conf: ConfigRef; cfile: string, createSubDir: bool = tru
|
||||
|
||||
proc toObjFile*(conf: ConfigRef; filename: string): string =
|
||||
# Object file for compilation
|
||||
#if filename.endsWith(".cpp"):
|
||||
# result = changeFileExt(filename, "cpp." & CC[cCompiler].objExt)
|
||||
#else:
|
||||
result = changeFileExt(filename, CC[conf.cCompiler].objExt)
|
||||
result = filename & "." & CC[conf.cCompiler].objExt
|
||||
|
||||
proc addFileToCompile*(conf: ConfigRef; cf: Cfile) =
|
||||
conf.toCompile.add(cf)
|
||||
|
||||
@@ -45,7 +45,8 @@ type
|
||||
hintExecuting, hintLinking, hintDependency,
|
||||
hintSource, hintPerformance, hintStackTrace, hintGCStats,
|
||||
hintGlobalVar,
|
||||
hintUser, hintUserRaw
|
||||
hintUser, hintUserRaw,
|
||||
hintExtendedContext
|
||||
|
||||
const
|
||||
MsgKindToStr*: array[TMsgKind, string] = [
|
||||
@@ -116,7 +117,9 @@ const
|
||||
hintGCStats: "$1",
|
||||
hintGlobalVar: "global variable declared here",
|
||||
hintUser: "$1",
|
||||
hintUserRaw: "$1"]
|
||||
hintUserRaw: "$1",
|
||||
hintExtendedContext: "$1",
|
||||
]
|
||||
|
||||
const
|
||||
WarningsToStr* = ["CannotOpenFile", "OctalEscape",
|
||||
@@ -132,12 +135,14 @@ const
|
||||
"GcMem", "Destructor", "LockLevel", "ResultShadowed",
|
||||
"Spacing", "User"]
|
||||
|
||||
HintsToStr* = ["Success", "SuccessX", "LineTooLong",
|
||||
HintsToStr* = [
|
||||
"Success", "SuccessX", "LineTooLong",
|
||||
"XDeclaredButNotUsed", "ConvToBaseNotNeeded", "ConvFromXtoItselfNotNeeded",
|
||||
"ExprAlwaysX", "QuitCalled", "Processing", "CodeBegin", "CodeEnd", "Conf",
|
||||
"Path", "CondTrue", "Name", "Pattern", "Exec", "Link", "Dependency",
|
||||
"Source", "Performance", "StackTrace", "GCStats", "GlobalVar",
|
||||
"User", "UserRaw"]
|
||||
"User", "UserRaw", "ExtendedContext",
|
||||
]
|
||||
|
||||
const
|
||||
fatalMin* = errUnknown
|
||||
@@ -157,30 +162,17 @@ type
|
||||
TNoteKind* = range[warnMin..hintMax] # "notes" are warnings or hints
|
||||
TNoteKinds* = set[TNoteKind]
|
||||
|
||||
const
|
||||
NotesVerbosity*: array[0..3, TNoteKinds] = [
|
||||
{low(TNoteKind)..high(TNoteKind)} - {warnShadowIdent, warnUninit,
|
||||
warnProveField, warnProveIndex,
|
||||
warnGcUnsafe,
|
||||
hintSuccessX, hintPath, hintConf,
|
||||
hintProcessing, hintPattern,
|
||||
hintDependency,
|
||||
hintExecuting, hintLinking,
|
||||
hintCodeBegin, hintCodeEnd,
|
||||
hintSource, hintStackTrace,
|
||||
hintGlobalVar, hintGCStats},
|
||||
{low(TNoteKind)..high(TNoteKind)} - {warnShadowIdent, warnUninit,
|
||||
warnProveField, warnProveIndex,
|
||||
warnGcUnsafe,
|
||||
hintPath,
|
||||
hintDependency,
|
||||
hintCodeBegin, hintCodeEnd,
|
||||
hintSource, hintStackTrace,
|
||||
hintGlobalVar, hintGCStats},
|
||||
{low(TNoteKind)..high(TNoteKind)} - {hintStackTrace, warnUninit},
|
||||
{low(TNoteKind)..high(TNoteKind)}]
|
||||
proc computeNotesVerbosity(): array[0..3, TNoteKinds] =
|
||||
result[3] = {low(TNoteKind)..high(TNoteKind)} - {}
|
||||
result[2] = result[3] - {hintStackTrace, warnUninit, hintExtendedContext}
|
||||
result[1] = result[2] - {warnShadowIdent, warnProveField, warnProveIndex,
|
||||
warnGcUnsafe, hintPath, hintDependency, hintCodeBegin, hintCodeEnd,
|
||||
hintSource, hintGlobalVar, hintGCStats}
|
||||
result[0] = result[1] - {hintSuccessX, hintConf, hintProcessing,
|
||||
hintPattern, hintExecuting, hintLinking}
|
||||
|
||||
const
|
||||
NotesVerbosity* = computeNotesVerbosity()
|
||||
errXMustBeCompileTime* = "'$1' can only be used in compile-time context"
|
||||
errArgsNeedRunOption* = "arguments can only be given if the '--run' option is selected"
|
||||
|
||||
|
||||
@@ -189,7 +189,7 @@ proc putComment(g: var TSrcGen, s: string) =
|
||||
put(g, tkComment, com)
|
||||
com = "## "
|
||||
inc(i)
|
||||
if i < s.len and s[i] == '\x0A': inc(i)
|
||||
if i <= hi and s[i] == '\x0A': inc(i)
|
||||
optNL(g, ind)
|
||||
of '\x0A':
|
||||
put(g, tkComment, com)
|
||||
@@ -226,7 +226,7 @@ proc maxLineLength(s: string): int =
|
||||
break
|
||||
of '\x0D':
|
||||
inc(i)
|
||||
if s[i] == '\x0A': inc(i)
|
||||
if i <= hi and s[i] == '\x0A': inc(i)
|
||||
result = max(result, lineLen)
|
||||
lineLen = 0
|
||||
of '\x0A':
|
||||
@@ -247,7 +247,7 @@ proc putRawStr(g: var TSrcGen, kind: TTokType, s: string) =
|
||||
put(g, kind, str)
|
||||
str = ""
|
||||
inc(i)
|
||||
if (i <= hi) and (s[i] == '\x0A'): inc(i)
|
||||
if i <= hi and s[i] == '\x0A': inc(i)
|
||||
optNL(g, 0)
|
||||
of '\x0A':
|
||||
put(g, kind, str)
|
||||
@@ -416,7 +416,7 @@ proc lsub(g: TSrcGen; n: PNode): int =
|
||||
of nkCast: result = lsub(g, n.sons[0]) + lsub(g, n.sons[1]) + len("cast[]()")
|
||||
of nkAddr: result = (if n.len>0: lsub(g, n.sons[0]) + len("addr()") else: 4)
|
||||
of nkStaticExpr: result = lsub(g, n.sons[0]) + len("static_")
|
||||
of nkHiddenAddr, nkHiddenDeref: result = lsub(g, n.sons[0])
|
||||
of nkHiddenAddr, nkHiddenDeref, nkStringToCString, nkCStringToString: result = lsub(g, n.sons[0])
|
||||
of nkCommand: result = lsub(g, n.sons[0]) + lcomma(g, n, 1) + 1
|
||||
of nkExprEqExpr, nkAsgn, nkFastAsgn: result = lsons(g, n) + 3
|
||||
of nkPar, nkCurly, nkBracket, nkClosure: result = lcomma(g, n) + 2
|
||||
@@ -446,7 +446,7 @@ proc lsub(g: TSrcGen; n: PNode): int =
|
||||
of nkChckRangeF: result = len("chckRangeF") + 2 + lcomma(g, n)
|
||||
of nkChckRange64: result = len("chckRange64") + 2 + lcomma(g, n)
|
||||
of nkChckRange: result = len("chckRange") + 2 + lcomma(g, n)
|
||||
of nkObjDownConv, nkObjUpConv, nkStringToCString, nkCStringToString:
|
||||
of nkObjDownConv, nkObjUpConv:
|
||||
result = 2
|
||||
if sonsLen(n) >= 1: result = result + lsub(g, n.sons[0])
|
||||
result = result + lcomma(g, n, 1)
|
||||
@@ -968,7 +968,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
|
||||
put(g, tkParLe, "(")
|
||||
gcomma(g, n)
|
||||
put(g, tkParRi, ")")
|
||||
of nkObjDownConv, nkObjUpConv, nkStringToCString, nkCStringToString:
|
||||
of nkObjDownConv, nkObjUpConv:
|
||||
if sonsLen(n) >= 1: gsub(g, n.sons[0])
|
||||
put(g, tkParLe, "(")
|
||||
gcomma(g, n, 1)
|
||||
@@ -1020,7 +1020,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
|
||||
of nkBind:
|
||||
putWithSpace(g, tkBind, "bind")
|
||||
gsub(g, n, 0)
|
||||
of nkCheckedFieldExpr, nkHiddenAddr, nkHiddenDeref:
|
||||
of nkCheckedFieldExpr, nkHiddenAddr, nkHiddenDeref, nkStringToCString, nkCStringToString:
|
||||
gsub(g, n, 0)
|
||||
of nkLambda:
|
||||
putWithSpace(g, tkProc, "proc")
|
||||
@@ -1073,9 +1073,13 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
|
||||
elif n[0].kind == nkSym: n[0].sym.name
|
||||
elif n[0].kind in {nkOpenSymChoice, nkClosedSymChoice}: n[0][0].sym.name
|
||||
else: nil
|
||||
if n[1].kind == nkPrefix or (opr != nil and renderer.isKeyword(opr)):
|
||||
var n_next = n[1]
|
||||
while n_next.kind in {nkCheckedFieldExpr, nkHiddenAddr, nkHiddenDeref,
|
||||
nkStringToCString, nkCStringToString} and n_next.len > 0:
|
||||
n_next = n_next[0]
|
||||
if n_next.kind == nkPrefix or (opr != nil and renderer.isKeyword(opr)):
|
||||
put(g, tkSpaces, Space)
|
||||
if n.sons[1].kind == nkInfix:
|
||||
if n_next.kind == nkInfix:
|
||||
put(g, tkParLe, "(")
|
||||
gsub(g, n.sons[1])
|
||||
put(g, tkParRi, ")")
|
||||
|
||||
@@ -610,14 +610,21 @@ proc myProcess(context: PPassContext, n: PNode): PNode =
|
||||
proc testExamples(c: PContext) =
|
||||
let inp = toFullPath(c.config, c.module.info)
|
||||
let outp = inp.changeFileExt"" & "_examples.nim"
|
||||
let nimcache = outp.changeFileExt"" & "_nimcache"
|
||||
renderModule(c.runnableExamples, inp, outp)
|
||||
let backend = if isDefined(c.config, "js"): "js"
|
||||
elif isDefined(c.config, "cpp"): "cpp"
|
||||
elif isDefined(c.config, "objc"): "objc"
|
||||
else: "c"
|
||||
if os.execShellCmd(os.getAppFilename() & " " & backend & " -r " & outp) != 0:
|
||||
if os.execShellCmd(os.getAppFilename() & " " & backend & " --nimcache:" & nimcache & " -r " & outp) != 0:
|
||||
quit "[Examples] failed"
|
||||
removeFile(outp)
|
||||
else:
|
||||
removeFile(outp)
|
||||
removeFile(outp.changeFileExt(ExeExt))
|
||||
try:
|
||||
removeDir(nimcache)
|
||||
except OSError:
|
||||
discard
|
||||
|
||||
proc myClose(graph: ModuleGraph; context: PPassContext, n: PNode): PNode =
|
||||
var c = PContext(context)
|
||||
|
||||
@@ -907,8 +907,11 @@ proc buildEchoStmt(c: PContext, n: PNode): PNode =
|
||||
result = semExpr(c, result)
|
||||
|
||||
proc semExprNoType(c: PContext, n: PNode): PNode =
|
||||
let isPush = hintExtendedContext in c.config.notes
|
||||
if isPush: pushInfoContext(c.config, n.info)
|
||||
result = semExpr(c, n, {efWantStmt})
|
||||
discardCheck(c, result)
|
||||
if isPush: popInfoContext(c.config)
|
||||
|
||||
proc isTypeExpr(n: PNode): bool =
|
||||
case n.kind
|
||||
|
||||
@@ -171,7 +171,7 @@ proc semTypeTraits(c: PContext, n: PNode): PNode =
|
||||
proc semOrd(c: PContext, n: PNode): PNode =
|
||||
result = n
|
||||
let parType = n.sons[1].typ
|
||||
if isOrdinalType(parType):
|
||||
if isOrdinalType(parType, allowEnumWithHoles=true):
|
||||
discard
|
||||
elif parType.kind == tySet:
|
||||
result.typ = makeRangeType(c, firstOrd(c.config, parType), lastOrd(c.config, parType), n.info)
|
||||
|
||||
@@ -552,6 +552,10 @@ proc isOwnedProcVar(n: PNode; owner: PSym): bool =
|
||||
# XXX prove the soundness of this effect system rule
|
||||
result = n.kind == nkSym and n.sym.kind == skParam and owner == n.sym.owner
|
||||
|
||||
proc isNoEffectList(n: PNode): bool {.inline.} =
|
||||
assert n.kind == nkEffectList
|
||||
n.len == 0 or (n[tagEffects] == nil and n[exceptionEffects] == nil)
|
||||
|
||||
proc trackOperand(tracked: PEffects, n: PNode, paramType: PType) =
|
||||
let a = skipConvAndClosure(n)
|
||||
let op = a.typ
|
||||
@@ -561,7 +565,7 @@ proc trackOperand(tracked: PEffects, n: PNode, paramType: PType) =
|
||||
let s = n.skipConv
|
||||
if s.kind == nkSym and s.sym.kind in routineKinds:
|
||||
propagateEffects(tracked, n, s.sym)
|
||||
elif effectList.len == 0:
|
||||
elif isNoEffectList(effectList):
|
||||
if isForwardedProc(n):
|
||||
# we have no explicit effects but it's a forward declaration and so it's
|
||||
# stated there are no additional effects, so simply propagate them:
|
||||
@@ -723,7 +727,7 @@ proc track(tracked: PEffects, n: PNode) =
|
||||
var effectList = op.n.sons[0]
|
||||
if a.kind == nkSym and a.sym.kind == skMethod:
|
||||
propagateEffects(tracked, n, a.sym)
|
||||
elif effectList.len == 0:
|
||||
elif isNoEffectList(effectList):
|
||||
if isForwardedProc(a):
|
||||
propagateEffects(tracked, n, a.sym)
|
||||
elif isIndirectCall(a, tracked.owner):
|
||||
@@ -781,6 +785,15 @@ proc track(tracked: PEffects, n: PNode) =
|
||||
initVar(tracked, child.sons[i], volatileCheck=false)
|
||||
addAsgnFact(tracked.guards, child.sons[i], last)
|
||||
notNilCheck(tracked, last, child.sons[i].typ)
|
||||
elif child.kind == nkVarTuple and last.kind != nkEmpty:
|
||||
for i in 0 .. child.len-2:
|
||||
if child[i].kind == nkEmpty or
|
||||
child[i].kind == nkSym and child[i].sym.name.s == "_":
|
||||
continue
|
||||
initVar(tracked, child[i], volatileCheck=false)
|
||||
if last.kind in {nkPar, nkTupleConstr}:
|
||||
addAsgnFact(tracked.guards, child[i], last[i])
|
||||
notNilCheck(tracked, last[i], child[i].typ)
|
||||
# since 'var (a, b): T = ()' is not even allowed, there is always type
|
||||
# inference for (a, b) and thus no nil checking is necessary.
|
||||
of nkConstSection:
|
||||
@@ -897,19 +910,18 @@ proc checkMethodEffects*(g: ModuleGraph; disp, branch: PSym) =
|
||||
[$branch.typ.lockLevel, $disp.typ.lockLevel])
|
||||
|
||||
proc setEffectsForProcType*(g: ModuleGraph; t: PType, n: PNode) =
|
||||
var effects = t.n.sons[0]
|
||||
var effects = t.n[0]
|
||||
if t.kind != tyProc or effects.kind != nkEffectList: return
|
||||
|
||||
let
|
||||
raisesSpec = effectSpec(n, wRaises)
|
||||
tagsSpec = effectSpec(n, wTags)
|
||||
if not isNil(raisesSpec) or not isNil(tagsSpec):
|
||||
if n.kind != nkEmpty:
|
||||
internalAssert g.config, effects.len == 0
|
||||
newSeq(effects.sons, effectListLen)
|
||||
let raisesSpec = effectSpec(n, wRaises)
|
||||
if not isNil(raisesSpec):
|
||||
effects.sons[exceptionEffects] = raisesSpec
|
||||
effects[exceptionEffects] = raisesSpec
|
||||
let tagsSpec = effectSpec(n, wTags)
|
||||
if not isNil(tagsSpec):
|
||||
effects.sons[tagEffects] = tagsSpec
|
||||
effects[tagEffects] = tagsSpec
|
||||
effects[pragmasEffects] = n
|
||||
|
||||
proc initEffects(g: ModuleGraph; effects: PNode; s: PSym; t: var TEffects) =
|
||||
newSeq(effects.sons, effectListLen)
|
||||
@@ -917,6 +929,7 @@ proc initEffects(g: ModuleGraph; effects: PNode; s: PSym; t: var TEffects) =
|
||||
effects.sons[tagEffects] = newNodeI(nkArgList, s.info)
|
||||
effects.sons[usesEffects] = g.emptyNode
|
||||
effects.sons[writeEffects] = g.emptyNode
|
||||
effects.sons[pragmasEffects] = g.emptyNode
|
||||
|
||||
t.exc = effects.sons[exceptionEffects]
|
||||
t.tags = effects.sons[tagEffects]
|
||||
|
||||
@@ -557,7 +557,10 @@ proc semCaseBranch(c: PContext, t, branch: PNode, branchIndex: int,
|
||||
return
|
||||
elif r.kind notin {nkCurly, nkBracket} or len(r) == 0:
|
||||
checkMinSonsLen(t, 1, c.config)
|
||||
branch.sons[i] = skipConv(fitNode(c, t.sons[0].typ, r, r.info))
|
||||
var tmp = fitNode(c, t.sons[0].typ, r, r.info)
|
||||
# the call to fitNode may introduce a call to a converter
|
||||
if tmp.kind in {nkHiddenCallConv}: tmp = semConstExpr(c, tmp)
|
||||
branch.sons[i] = skipConv(tmp)
|
||||
inc(covered)
|
||||
else:
|
||||
if r.kind == nkCurly:
|
||||
@@ -1341,6 +1344,10 @@ proc semTypeClass(c: PContext, n: PNode, prev: PType): PType =
|
||||
dummyName = param
|
||||
dummyType = candidateTypeSlot
|
||||
|
||||
# this can be true for 'nim check' on incomplete concepts,
|
||||
# see bug #8230
|
||||
if dummyName.kind == nkEmpty: continue
|
||||
|
||||
internalAssert c.config, dummyName.kind == nkIdent
|
||||
var dummyParam = newSym(if modifier == tyTypeDesc: skType else: skVar,
|
||||
dummyName.ident, owner, param.info)
|
||||
|
||||
@@ -163,7 +163,7 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]) =
|
||||
c.hashType t.sons[i], flags
|
||||
else:
|
||||
c.hashType t.lastSon, flags
|
||||
of tyAlias, tySink, tyUserTypeClasses:
|
||||
of tyAlias, tySink, tyUserTypeClasses, tyInferred:
|
||||
c.hashType t.lastSon, flags
|
||||
of tyBool, tyChar, tyInt..tyUInt64:
|
||||
# no canonicalization for integral types, so that e.g. ``pid_t`` is
|
||||
|
||||
@@ -569,7 +569,10 @@ proc procParamTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
|
||||
# signature. There is a change that the target
|
||||
# type is already fully-determined, so we are
|
||||
# going to try resolve it
|
||||
f = generateTypeInstance(c.c, c.bindings, c.call.info, f)
|
||||
if c.call != nil:
|
||||
f = generateTypeInstance(c.c, c.bindings, c.call.info, f)
|
||||
else:
|
||||
f = nil
|
||||
if f == nil or f.isMetaType:
|
||||
# no luck resolving the type, so the inference fails
|
||||
return isBothMetaConvertible
|
||||
@@ -637,7 +640,7 @@ proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
|
||||
else: discard
|
||||
|
||||
proc typeRangeRel(f, a: PType): TTypeRelation {.noinline.} =
|
||||
template checkRange[T](a0, a1, f0, f1: T): TTypeRelation =
|
||||
template checkRange[T](a0, a1, f0, f1: T): TTypeRelation =
|
||||
if a0 == f0 and a1 == f1:
|
||||
isEqual
|
||||
elif a0 >= f0 and a1 <= f1:
|
||||
@@ -647,12 +650,12 @@ proc typeRangeRel(f, a: PType): TTypeRelation {.noinline.} =
|
||||
isConvertible
|
||||
else:
|
||||
isNone
|
||||
|
||||
if f.isOrdinalType:
|
||||
|
||||
if f.isOrdinalType:
|
||||
checkRange(firstOrd(nil, a), lastOrd(nil, a), firstOrd(nil, f), lastOrd(nil, f))
|
||||
else:
|
||||
else:
|
||||
checkRange(firstFloat(a), lastFloat(a), firstFloat(f), lastFloat(f))
|
||||
|
||||
|
||||
|
||||
proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType =
|
||||
var
|
||||
@@ -1801,7 +1804,7 @@ proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType,
|
||||
# 'f <- dest' in order to not break the unification:
|
||||
# see tests/tgenericconverter:
|
||||
let srca = typeRel(m, src, a)
|
||||
if srca notin {isEqual, isGeneric}: continue
|
||||
if srca notin {isEqual, isGeneric, isSubtype}: continue
|
||||
|
||||
let destIsGeneric = containsGenericType(dest)
|
||||
if destIsGeneric:
|
||||
@@ -1814,7 +1817,12 @@ proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType,
|
||||
s.info = arg.info
|
||||
result = newNodeIT(nkHiddenCallConv, arg.info, dest)
|
||||
addSon(result, s)
|
||||
addSon(result, copyTree(arg))
|
||||
var param: PNode = nil
|
||||
if srca == isSubtype:
|
||||
param = implicitConv(nkHiddenSubConv, src, copyTree(arg), m, c)
|
||||
else:
|
||||
param = copyTree(arg)
|
||||
addSon(result, param)
|
||||
inc(m.convMatches)
|
||||
m.genericConverter = srca == isGeneric or destIsGeneric
|
||||
return result
|
||||
@@ -2231,6 +2239,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
|
||||
m.state = csNoMatch
|
||||
return
|
||||
m.baseTypeMatch = false
|
||||
m.typedescMatched = false
|
||||
n.sons[a].sons[1] = prepareOperand(c, formal.typ, n.sons[a].sons[1])
|
||||
n.sons[a].typ = n.sons[a].sons[1].typ
|
||||
var arg = paramTypesMatch(m, formal.typ, n.sons[a].typ,
|
||||
@@ -2266,6 +2275,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
|
||||
# beware of the side-effects in 'prepareOperand'! So only do it for
|
||||
# varargs matching. See tests/metatype/tstatic_overloading.
|
||||
m.baseTypeMatch = false
|
||||
m.typedescMatched = false
|
||||
incl(marker, formal.position)
|
||||
n.sons[a] = prepareOperand(c, formal.typ, n.sons[a])
|
||||
var arg = paramTypesMatch(m, formal.typ, n.sons[a].typ,
|
||||
@@ -2300,6 +2310,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
|
||||
addSon(container, n.sons[a])
|
||||
else:
|
||||
m.baseTypeMatch = false
|
||||
m.typedescMatched = false
|
||||
n.sons[a] = prepareOperand(c, formal.typ, n.sons[a])
|
||||
var arg = paramTypesMatch(m, formal.typ, n.sons[a].typ,
|
||||
n.sons[a], nOrig.sons[a])
|
||||
|
||||
@@ -133,18 +133,18 @@ proc elemType*(t: PType): PType =
|
||||
else: result = t.lastSon
|
||||
assert(result != nil)
|
||||
|
||||
proc isOrdinalType*(t: PType): bool =
|
||||
proc enumHasHoles*(t: PType): bool =
|
||||
var b = t.skipTypes({tyRange, tyGenericInst, tyAlias, tySink})
|
||||
result = b.kind == tyEnum and tfEnumHasHoles in b.flags
|
||||
|
||||
proc isOrdinalType*(t: PType, allowEnumWithHoles = false): bool =
|
||||
assert(t != nil)
|
||||
const
|
||||
# caution: uint, uint64 are no ordinal types!
|
||||
baseKinds = {tyChar,tyInt..tyInt64,tyUInt8..tyUInt32,tyBool,tyEnum}
|
||||
parentKinds = {tyRange, tyOrdinal, tyGenericInst, tyAlias, tySink, tyDistinct}
|
||||
t.kind in baseKinds or (t.kind in parentKinds and isOrdinalType(t.sons[0]))
|
||||
|
||||
proc enumHasHoles*(t: PType): bool =
|
||||
var b = t
|
||||
while b.kind in {tyRange, tyGenericInst, tyAlias, tySink}: b = b.sons[0]
|
||||
result = b.kind == tyEnum and tfEnumHasHoles in b.flags
|
||||
(t.kind in baseKinds and not (t.enumHasHoles and not allowEnumWithHoles)) or
|
||||
(t.kind in parentKinds and isOrdinalType(t.lastSon))
|
||||
|
||||
proc iterOverTypeAux(marker: var IntSet, t: PType, iter: TTypeIter,
|
||||
closure: RootRef): bool
|
||||
|
||||
@@ -533,8 +533,10 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||
let s = regs[rb].node.strVal
|
||||
if s.isNil:
|
||||
stackTrace(c, tos, pc, errNilAccess)
|
||||
elif idx <=% s.len:
|
||||
elif idx <% s.len:
|
||||
regs[ra].intVal = s[idx].ord
|
||||
elif idx == s.len and optLaxStrings in c.config.options:
|
||||
regs[ra].intVal = 0
|
||||
else:
|
||||
stackTrace(c, tos, pc, errIndexOutOfBounds)
|
||||
of opcWrArr:
|
||||
|
||||
@@ -229,7 +229,8 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo;
|
||||
for i in 1..<t.sons.len:
|
||||
fp.add newIdentDefs(t.n[i], t.sons[i])
|
||||
result.add fp
|
||||
result.add newNodeI(nkEmpty, info) # pragmas aren't reconstructed yet
|
||||
result.add if t.n[0].len > 0: t.n[0][pragmasEffects].copyTree
|
||||
else: newNodeI(nkEmpty, info)
|
||||
else:
|
||||
result = mapTypeToBracket("proc", mNone, t, info)
|
||||
of tyOpenArray: result = mapTypeToBracket("openArray", mOpenArray, t, info)
|
||||
|
||||
@@ -111,10 +111,10 @@ path="$lib/pure"
|
||||
|
||||
@if nintendoswitch:
|
||||
cc = "switch_gcc"
|
||||
switch_gcc.options.linker = "-g -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE $SWITCH_LIBS"
|
||||
switch_gcc.cpp.options.linker = "-g -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE $SWITCH_LIBS"
|
||||
switch_gcc.options.always = "-g -Wall -O2 -ffunction-sections -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE $SWITCH_INCLUDES -D__SWITCH__"
|
||||
switch_gcc.cpp.options.always = "-g -Wall -O2 -ffunction-sections -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE $SWITCH_INCLUDES -D__SWITCH__ -fno-rtti -fno-exceptions -std=gnu++11"
|
||||
switch_gcc.options.linker = "-g -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE"
|
||||
switch_gcc.cpp.options.linker = "-g -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE"
|
||||
switch_gcc.options.always = "-g -Wall -O2 -ffunction-sections -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE -D__SWITCH__"
|
||||
switch_gcc.cpp.options.always = "-g -Wall -O2 -ffunction-sections -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE -D__SWITCH__ -fno-rtti -fno-exceptions -std=gnu++11"
|
||||
@end
|
||||
|
||||
# Configuration for the Intel C/C++ compiler:
|
||||
|
||||
@@ -1330,15 +1330,6 @@ dt pre > span.Operator ~ span.Identifier {
|
||||
background-repeat: no-repeat;
|
||||
background-image: url("data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA==");
|
||||
margin-bottom: -5px; }
|
||||
div.pragma {
|
||||
display: none;
|
||||
}
|
||||
span.pragmabegin {
|
||||
cursor: pointer;
|
||||
}
|
||||
span.pragmaend {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
div.search_results {
|
||||
background-color: antiquewhite;
|
||||
@@ -1351,32 +1342,47 @@ div#global-links ul {
|
||||
margin-left: 0;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
span.pragmadots {
|
||||
/* Position: relative frees us up to make the dots
|
||||
look really nice without fucking up the layout and
|
||||
causing bulging in the parent container */
|
||||
position: relative;
|
||||
/* 1px down looks slightly nicer */
|
||||
top: 1px;
|
||||
|
||||
padding: 2px;
|
||||
background-color: #D3D3D3;
|
||||
border-radius: 4px;
|
||||
margin: 0 2px;
|
||||
cursor: pointer;
|
||||
|
||||
/* For some reason on Chrome, making the font size
|
||||
smaller than 1em is causing the parent container to
|
||||
bulge slightly. So, we're stuck with inheriting 1em,
|
||||
which is sad, because 0.8em looks better... */
|
||||
}
|
||||
span.pragmadots:hover {
|
||||
background-color: #DBDBDB;
|
||||
}
|
||||
span.pragmawrap {
|
||||
display: none;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<script type="text/javascript" src="../dochack.js"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
function togglepragma(d) {
|
||||
if (d.style.display != 'inline')
|
||||
d.style.display = 'inline';
|
||||
else
|
||||
d.style.display = 'none';
|
||||
}
|
||||
|
||||
function main() {
|
||||
var elements = document.getElementsByClassName("pragmabegin");
|
||||
for (var i = 0; i < elements.length; ++i) {
|
||||
var e = elements[i];
|
||||
e.onclick = function(event) {
|
||||
togglepragma(event.target.nextSibling);
|
||||
};
|
||||
}
|
||||
var elements = document.getElementsByClassName("pragmaend");
|
||||
for (var i = 0; i < elements.length; ++i) {
|
||||
var e = elements[i];
|
||||
e.onclick = function(event) {
|
||||
togglepragma(event.target.previousSibling);
|
||||
};
|
||||
var pragmaDots = document.getElementsByClassName("pragmadots");
|
||||
for (var i = 0; i < pragmaDots.length; i++) {
|
||||
pragmaDots[i].onclick = function(event) {
|
||||
// Hide tease
|
||||
event.target.parentNode.style.display = "none";
|
||||
// Show actual
|
||||
event.target.parentNode.nextElementSibling.style.display = "inline";
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1270,10 +1270,10 @@ AST:
|
||||
nnkIdent("float32"),
|
||||
nnkEmpty()
|
||||
)
|
||||
nnkPragma(nnkIdent("inline")),
|
||||
nnkEmpty(), # reserved slot for future use
|
||||
nnkStmtList(nnkDiscardStmt(nnkEmpty())) # the meat of the proc
|
||||
)
|
||||
),
|
||||
nnkPragma(nnkIdent("inline")),
|
||||
nnkEmpty(), # reserved slot for future use
|
||||
nnkStmtList(nnkDiscardStmt(nnkEmpty())) # the meat of the proc
|
||||
)
|
||||
|
||||
There is another consideration. Nim has flexible type identification for
|
||||
|
||||
@@ -12,7 +12,8 @@ Options:
|
||||
-p, --path:PATH add path to search paths
|
||||
-d, --define:SYMBOL(:VAL)
|
||||
define a conditional symbol
|
||||
(Optionally: Define the value for that symbol)
|
||||
(Optionally: Define the value for that symbol,
|
||||
see: "compile time define pragmas")
|
||||
-u, --undef:SYMBOL undefine a conditional symbol
|
||||
-f, --forceBuild force rebuilding of all modules
|
||||
--stackTrace:on|off turn stack tracing on|off
|
||||
|
||||
@@ -253,7 +253,7 @@ the exact spelling of an identifier. The exception with respect to the first
|
||||
letter allows common code like ``var foo: Foo`` to be parsed unambiguously.
|
||||
|
||||
Historically, Nim was a fully `style-insensitive`:idx: language. This meant that
|
||||
it was not case-sensitive and underscores were ignored and there was no even a
|
||||
it was not case-sensitive and underscores were ignored and there was not even a
|
||||
distinction between ``foo`` and ``Foo``.
|
||||
|
||||
|
||||
|
||||
12
doc/nimc.rst
12
doc/nimc.rst
@@ -232,12 +232,12 @@ Cross compilation for Nintendo Switch
|
||||
=====================================
|
||||
|
||||
Simply add --os:nintendoswitch
|
||||
to your usual ``nim c`` or ``nim cpp`` command and set the ``SWITCH_LIBS``
|
||||
and ``SWITCH_INCLUDES`` environment variables to something like:
|
||||
to your usual ``nim c`` or ``nim cpp`` command and set the ``passC``
|
||||
and ``passL`` command line switches to something like:
|
||||
|
||||
.. code-block:: console
|
||||
export SWITCH_INCLUDES="-I$DEVKITPRO/libnx/include"
|
||||
export SWITCH_LIBS="-specs=$DEVKITPRO/libnx/switch.specs -L$DEVKITPRO/libnx/lib -lnx"
|
||||
nim c ... --passC="-I$DEVKITPRO/libnx/include" ...
|
||||
--passL="-specs=$DEVKITPRO/libnx/switch.specs -L$DEVKITPRO/libnx/lib -lnx"
|
||||
|
||||
or setup a nim.cfg file like so:
|
||||
|
||||
@@ -259,10 +259,6 @@ an nro file with the ``elf2nro`` tool in the DevkitPro release. Examples can be
|
||||
`the nim-libnx github repo <https://github.com/jyapayne/nim-libnx.git>`_ or you can use
|
||||
`the switch builder tool <https://github.com/jyapayne/switch-builder.git>`_.
|
||||
|
||||
Environment variables are:
|
||||
- ``SWITCH_LIBS`` for any extra libraries required by your application (``-lLIBNAME`` or ``-LLIBPATH``)
|
||||
- ``SWITCH_INCLUDES`` for any extra include files (``-IINCLUDE_PATH``)
|
||||
|
||||
There are a few things that don't work because the DevkitPro libraries don't support them.
|
||||
They are:
|
||||
|
||||
|
||||
@@ -891,7 +891,7 @@ important differences:
|
||||
future version of the compiler.)
|
||||
|
||||
However, you can also use a ``closure`` iterator to get a different set of
|
||||
restrictions. See `first class iterators <manual.html#first-class-iterators>`_
|
||||
restrictions. See `first class iterators <manual.html#iterators-and-the-for-statement-first-class-iterators>`_
|
||||
for details. Iterators can have the same name and parameters as a proc, since
|
||||
essentially they have their own namespaces. Therefore it is common practice to
|
||||
wrap iterators in procs of the same name which accumulate the result of the
|
||||
|
||||
@@ -1284,7 +1284,9 @@ proc customPragmaNode(n: NimNode): NimNode =
|
||||
let
|
||||
typ = n.getTypeInst()
|
||||
|
||||
if typ.typeKind == ntyTypeDesc:
|
||||
if typ.kind == nnkBracketExpr and typ.len > 1 and typ[1].kind == nnkProcTy:
|
||||
return typ[1][1]
|
||||
elif typ.typeKind == ntyTypeDesc:
|
||||
let impl = typ[1].getImpl()
|
||||
if impl[0].kind == nnkPragmaExpr:
|
||||
return impl[0][1]
|
||||
|
||||
@@ -272,7 +272,7 @@ proc asyncSockHandleWrite(h: RootRef) =
|
||||
AsyncSocket(h).deleg.mode = fmRead
|
||||
|
||||
when defined(ssl):
|
||||
proc asyncSockDoHandshake(h: PObject) {.gcsafe.} =
|
||||
proc asyncSockDoHandshake(h: RootRef) {.gcsafe.} =
|
||||
if AsyncSocket(h).socket.isSSL and not
|
||||
AsyncSocket(h).socket.gotHandshake:
|
||||
if AsyncSocket(h).sslNeedAccept:
|
||||
|
||||
@@ -122,6 +122,21 @@ var
|
||||
B9600* {.importc, header: "<termios.h>".}: Speed
|
||||
B19200* {.importc, header: "<termios.h>".}: Speed
|
||||
B38400* {.importc, header: "<termios.h>".}: Speed
|
||||
B57600* {.importc, header: "<termios.h>".}: Speed
|
||||
B115200* {.importc, header: "<termios.h>".}: Speed
|
||||
B230400* {.importc, header: "<termios.h>".}: Speed
|
||||
B460800* {.importc, header: "<termios.h>".}: Speed
|
||||
B500000* {.importc, header: "<termios.h>".}: Speed
|
||||
B576000* {.importc, header: "<termios.h>".}: Speed
|
||||
B921600* {.importc, header: "<termios.h>".}: Speed
|
||||
B1000000* {.importc, header: "<termios.h>".}: Speed
|
||||
B1152000* {.importc, header: "<termios.h>".}: Speed
|
||||
B1500000* {.importc, header: "<termios.h>".}: Speed
|
||||
B2000000* {.importc, header: "<termios.h>".}: Speed
|
||||
B2500000* {.importc, header: "<termios.h>".}: Speed
|
||||
B3000000* {.importc, header: "<termios.h>".}: Speed
|
||||
B3500000* {.importc, header: "<termios.h>".}: Speed
|
||||
B4000000* {.importc, header: "<termios.h>".}: Speed
|
||||
EXTA* {.importc, header: "<termios.h>".}: Speed
|
||||
EXTB* {.importc, header: "<termios.h>".}: Speed
|
||||
CSIZE* {.importc, header: "<termios.h>".}: Cflag
|
||||
|
||||
@@ -1890,7 +1890,7 @@ proc entityToUtf8*(entity: string): string =
|
||||
proc addNode(father, son: XmlNode) =
|
||||
if son != nil: add(father, son)
|
||||
|
||||
proc parse(x: var XmlParser, errors: var seq[string]): XmlNode
|
||||
proc parse(x: var XmlParser, errors: var seq[string]): XmlNode {.gcsafe.}
|
||||
|
||||
proc expected(x: var XmlParser, n: XmlNode): string =
|
||||
result = errorMsg(x, "</" & n.tag & "> expected")
|
||||
|
||||
@@ -66,7 +66,7 @@ proc raiseOSError*(errorCode: OSErrorCode; additionalInfo = "") {.noinline.} =
|
||||
raise e
|
||||
|
||||
{.push stackTrace:off.}
|
||||
proc osLastError*(): OSErrorCode =
|
||||
proc osLastError*(): OSErrorCode {.sideEffect.} =
|
||||
## Retrieves the last operating system error code.
|
||||
##
|
||||
## This procedure is useful in the event when an OS call fails. In that case
|
||||
|
||||
@@ -322,8 +322,7 @@ type MemSlice* = object ## represent slice of a MemFile for iteration over deli
|
||||
|
||||
proc `==`*(x, y: MemSlice): bool =
|
||||
## Compare a pair of MemSlice for strict equality.
|
||||
proc memcmp(a, b: pointer, n:int):int {.importc: "memcmp",header: "string.h".}
|
||||
result = (x.size == y.size and memcmp(x.data, y.data, x.size) == 0)
|
||||
result = (x.size == y.size and equalMem(x.data, y.data, x.size))
|
||||
|
||||
proc `$`*(ms: MemSlice): string {.inline.} =
|
||||
## Return a Nim string built from a MemSlice.
|
||||
|
||||
@@ -189,7 +189,7 @@ proc `$`*[T](self: Option[T]): string =
|
||||
if self.isSome:
|
||||
"Some(" & $self.val & ")"
|
||||
else:
|
||||
"None[" & T.name & "]"
|
||||
"None[" & name(T) & "]"
|
||||
|
||||
when isMainModule:
|
||||
import unittest, sequtils
|
||||
@@ -298,3 +298,17 @@ when isMainModule:
|
||||
test "none[T]":
|
||||
check(none[int]().isNone)
|
||||
check(none(int) == none[int]())
|
||||
|
||||
test "$ on typed with .name":
|
||||
type Named = object
|
||||
name: string
|
||||
|
||||
let nobody = none(Named)
|
||||
check($nobody == "None[Named]")
|
||||
|
||||
test "$ on type with name()":
|
||||
type Person = object
|
||||
myname: string
|
||||
|
||||
let noperson = none(Person)
|
||||
check($noperson == "None[Person]")
|
||||
|
||||
@@ -296,6 +296,26 @@ proc setCurrentDir*(newDir: string) {.inline, tags: [].} =
|
||||
else:
|
||||
if chdir(newDir) != 0'i32: raiseOSError(osLastError())
|
||||
|
||||
proc absolutePath*(path: string, root = getCurrentDir()): string =
|
||||
## Returns the absolute path of `path`, rooted at `root` (which must be absolute)
|
||||
## if `path` is absolute, return it, ignoring `root`
|
||||
runnableExamples:
|
||||
doAssert absolutePath("a") == getCurrentDir() / "a"
|
||||
if isAbsolute(path): path
|
||||
else:
|
||||
if not root.isAbsolute:
|
||||
raise newException(ValueError, "The specified root is not absolute: " & root)
|
||||
joinPath(root, path)
|
||||
|
||||
when isMainModule:
|
||||
doAssertRaises(ValueError): discard absolutePath("a", "b")
|
||||
doAssert absolutePath("a") == getCurrentDir() / "a"
|
||||
doAssert absolutePath("a", "/b") == "/b" / "a"
|
||||
when defined(Posix):
|
||||
doAssert absolutePath("a", "/b/") == "/b" / "a"
|
||||
doAssert absolutePath("a", "/b/c") == "/b/c" / "a"
|
||||
doAssert absolutePath("/a", "b/") == "/a"
|
||||
|
||||
proc expandFilename*(filename: string): string {.rtl, extern: "nos$1",
|
||||
tags: [ReadDirEffect].} =
|
||||
## Returns the full (`absolute`:idx:) path of an existing file `filename`,
|
||||
|
||||
@@ -423,12 +423,22 @@ proc isAbsolute*(path: string): bool {.rtl, noSideEffect, extern: "nos$1".} =
|
||||
## Checks whether a given `path` is absolute.
|
||||
##
|
||||
## On Windows, network paths are considered absolute too.
|
||||
runnableExamples:
|
||||
doAssert(not "".isAbsolute)
|
||||
doAssert(not ".".isAbsolute)
|
||||
when defined(posix):
|
||||
doAssert "/".isAbsolute
|
||||
doAssert(not "a/".isAbsolute)
|
||||
|
||||
if len(path) == 0: return false
|
||||
|
||||
when doslikeFileSystem:
|
||||
var len = len(path)
|
||||
result = (len > 0 and path[0] in {'/', '\\'}) or
|
||||
result = (path[0] in {'/', '\\'}) or
|
||||
(len > 1 and path[0] in {'a'..'z', 'A'..'Z'} and path[1] == ':')
|
||||
elif defined(macos):
|
||||
result = path.len > 0 and path[0] != ':'
|
||||
# according to https://perldoc.perl.org/File/Spec/Mac.html `:a` is a relative path
|
||||
result = path[0] != ':'
|
||||
elif defined(RISCOS):
|
||||
result = path[0] == '$'
|
||||
elif defined(posix):
|
||||
|
||||
@@ -683,8 +683,8 @@ when isMainModule:
|
||||
# works:
|
||||
import times
|
||||
|
||||
var nullTime: DateTime
|
||||
check &"{nullTime:yyyy-mm-dd}", "0000-00-00"
|
||||
var dt = initDateTime(01, mJan, 2000, 00, 00, 00)
|
||||
check &"{dt:yyyy-MM-dd}", "2000-01-01"
|
||||
|
||||
var tm = fromUnix(0)
|
||||
discard &"{tm}"
|
||||
|
||||
@@ -19,22 +19,30 @@
|
||||
import macros
|
||||
import strformat
|
||||
from strutils import toLowerAscii
|
||||
import colors
|
||||
import colors, tables
|
||||
|
||||
const
|
||||
hasThreadSupport = compileOption("threads")
|
||||
when defined(windows):
|
||||
import winlean
|
||||
|
||||
when not hasThreadSupport:
|
||||
import tables
|
||||
var
|
||||
colorsFGCache = initTable[Color, string]()
|
||||
colorsBGCache = initTable[Color, string]()
|
||||
styleCache = initTable[int, string]()
|
||||
type
|
||||
PTerminal = ref object
|
||||
trueColorIsSupported: bool
|
||||
trueColorIsEnabled: bool
|
||||
fgSetColor: bool
|
||||
when defined(windows):
|
||||
hStdout: Handle
|
||||
hStderr: Handle
|
||||
oldStdoutAttr: int16
|
||||
oldStderrAttr: int16
|
||||
|
||||
var
|
||||
trueColorIsSupported: bool
|
||||
trueColorIsEnabled: bool
|
||||
fgSetColor: bool
|
||||
var gTerm {.threadvar.}: PTerminal
|
||||
|
||||
proc newTerminal(): PTerminal
|
||||
|
||||
proc getTerminal(): PTerminal {.inline.} =
|
||||
if isNil(gTerm):
|
||||
gTerm = newTerminal()
|
||||
result = gTerm
|
||||
|
||||
const
|
||||
fgPrefix = "\x1b[38;2;"
|
||||
@@ -156,23 +164,6 @@ when defined(windows):
|
||||
proc setConsoleMode(hConsoleHandle: Handle, dwMode: DWORD): WINBOOL{.
|
||||
stdcall, dynlib: "kernel32", importc: "SetConsoleMode".}
|
||||
|
||||
var
|
||||
hStdout: Handle # = createFile("CONOUT$", GENERIC_WRITE, 0, nil,
|
||||
# OPEN_ALWAYS, 0, 0)
|
||||
hStderr: Handle
|
||||
|
||||
block:
|
||||
var hStdoutTemp = getStdHandle(STD_OUTPUT_HANDLE)
|
||||
if duplicateHandle(getCurrentProcess(), hStdoutTemp, getCurrentProcess(),
|
||||
addr(hStdout), 0, 1, DUPLICATE_SAME_ACCESS) == 0:
|
||||
when defined(consoleapp):
|
||||
raiseOSError(osLastError())
|
||||
var hStderrTemp = getStdHandle(STD_ERROR_HANDLE)
|
||||
if duplicateHandle(getCurrentProcess(), hStderrTemp, getCurrentProcess(),
|
||||
addr(hStderr), 0, 1, DUPLICATE_SAME_ACCESS) == 0:
|
||||
when defined(consoleapp):
|
||||
raiseOSError(osLastError())
|
||||
|
||||
proc getCursorPos(h: Handle): tuple [x,y: int] =
|
||||
var c: CONSOLESCREENBUFFERINFO
|
||||
if getConsoleScreenBufferInfo(h, addr(c)) == 0:
|
||||
@@ -193,12 +184,23 @@ when defined(windows):
|
||||
return c.wAttributes
|
||||
return 0x70'i16 # ERROR: return white background, black text
|
||||
|
||||
var
|
||||
oldStdoutAttr = getAttributes(hStdout)
|
||||
oldStderrAttr = getAttributes(hStderr)
|
||||
proc initTerminal(term: PTerminal) =
|
||||
var hStdoutTemp = getStdHandle(STD_OUTPUT_HANDLE)
|
||||
if duplicateHandle(getCurrentProcess(), hStdoutTemp, getCurrentProcess(),
|
||||
addr(term.hStdout), 0, 1, DUPLICATE_SAME_ACCESS) == 0:
|
||||
when defined(consoleapp):
|
||||
raiseOSError(osLastError())
|
||||
var hStderrTemp = getStdHandle(STD_ERROR_HANDLE)
|
||||
if duplicateHandle(getCurrentProcess(), hStderrTemp, getCurrentProcess(),
|
||||
addr(term.hStderr), 0, 1, DUPLICATE_SAME_ACCESS) == 0:
|
||||
when defined(consoleapp):
|
||||
raiseOSError(osLastError())
|
||||
term.oldStdoutAttr = getAttributes(term.hStdout)
|
||||
term.oldStderrAttr = getAttributes(term.hStderr)
|
||||
|
||||
template conHandle(f: File): Handle =
|
||||
if f == stderr: hStderr else: hStdout
|
||||
let term = getTerminal()
|
||||
if f == stderr: term.hStderr else: term.hStdout
|
||||
|
||||
else:
|
||||
import termios, posix, os, parseutils
|
||||
@@ -306,7 +308,7 @@ proc setCursorPos*(f: File, x, y: int) =
|
||||
let h = conHandle(f)
|
||||
setCursorPos(h, x, y)
|
||||
else:
|
||||
f.write(fmt"{stylePrefix}{y};{x}f")
|
||||
f.write(fmt"{stylePrefix}{y+1};{x+1}f")
|
||||
|
||||
proc setCursorXPos*(f: File, x: int) =
|
||||
## Sets the terminal's cursor to the x position.
|
||||
@@ -321,7 +323,7 @@ proc setCursorXPos*(f: File, x: int) =
|
||||
if setConsoleCursorPosition(h, origin) == 0:
|
||||
raiseOSError(osLastError())
|
||||
else:
|
||||
f.write(fmt"{stylePrefix}{x}G")
|
||||
f.write(fmt"{stylePrefix}{x+1}G")
|
||||
|
||||
when defined(windows):
|
||||
proc setCursorYPos*(f: File, y: int) =
|
||||
@@ -459,10 +461,11 @@ proc eraseScreen*(f: File) =
|
||||
proc resetAttributes*(f: File) =
|
||||
## Resets all attributes.
|
||||
when defined(windows):
|
||||
let term = getTerminal()
|
||||
if f == stderr:
|
||||
discard setConsoleTextAttribute(hStderr, oldStderrAttr)
|
||||
discard setConsoleTextAttribute(term.hStderr, term.oldStderrAttr)
|
||||
else:
|
||||
discard setConsoleTextAttribute(hStdout, oldStdoutAttr)
|
||||
discard setConsoleTextAttribute(term.hStdout, term.oldStdoutAttr)
|
||||
else:
|
||||
f.write(ansiResetCode)
|
||||
|
||||
@@ -471,11 +474,12 @@ type
|
||||
styleBright = 1, ## bright text
|
||||
styleDim, ## dim text
|
||||
styleItalic, ## italic (or reverse on terminals not supporting)
|
||||
styleUnderscore = 4, ## underscored text
|
||||
styleUnderscore, ## underscored text
|
||||
styleBlink, ## blinking/bold text
|
||||
styleReverse = 7, ## reverse
|
||||
styleHidden ## hidden text
|
||||
styleStrikethrough, ## strikethrough
|
||||
styleBlinkRapid, ## rapid blinking/bold text (not widely supported)
|
||||
styleReverse, ## reverse
|
||||
styleHidden, ## hidden text
|
||||
styleStrikethrough ## strikethrough
|
||||
|
||||
{.deprecated: [TStyle: Style].}
|
||||
{.deprecated: [styleUnknown: styleItalic].}
|
||||
@@ -486,14 +490,7 @@ when not defined(windows):
|
||||
gBG {.threadvar.}: int
|
||||
|
||||
proc ansiStyleCode*(style: int): string =
|
||||
when hasThreadSupport:
|
||||
result = fmt"{stylePrefix}{style}m"
|
||||
else:
|
||||
if styleCache.hasKey(style):
|
||||
result = styleCache[style]
|
||||
else:
|
||||
result = fmt"{stylePrefix}{style}m"
|
||||
styleCache[style] = result
|
||||
result = fmt"{stylePrefix}{style}m"
|
||||
|
||||
template ansiStyleCode*(style: Style): string =
|
||||
ansiStyleCode(style.int)
|
||||
@@ -520,10 +517,11 @@ proc setStyle*(f: File, style: set[Style]) =
|
||||
proc writeStyled*(txt: string, style: set[Style] = {styleBright}) =
|
||||
## Writes the text `txt` in a given `style` to stdout.
|
||||
when defined(windows):
|
||||
var old = getAttributes(hStdout)
|
||||
let term = getTerminal()
|
||||
var old = getAttributes(term.hStdout)
|
||||
stdout.setStyle(style)
|
||||
stdout.write(txt)
|
||||
discard setConsoleTextAttribute(hStdout, old)
|
||||
discard setConsoleTextAttribute(term.hStdout, old)
|
||||
else:
|
||||
stdout.setStyle(style)
|
||||
stdout.write(txt)
|
||||
@@ -632,32 +630,16 @@ template ansiForegroundColorCode*(fg: static[ForegroundColor],
|
||||
ansiStyleCode(fg.int + bright.int * 60)
|
||||
|
||||
proc ansiForegroundColorCode*(color: Color): string =
|
||||
when hasThreadSupport:
|
||||
let rgb = extractRGB(color)
|
||||
result = fmt"{fgPrefix}{rgb.r};{rgb.g};{rgb.b}m"
|
||||
else:
|
||||
if colorsFGCache.hasKey(color):
|
||||
result = colorsFGCache[color]
|
||||
else:
|
||||
let rgb = extractRGB(color)
|
||||
result = fmt"{fgPrefix}{rgb.r};{rgb.g};{rgb.b}m"
|
||||
colorsFGCache[color] = result
|
||||
let rgb = extractRGB(color)
|
||||
result = fmt"{fgPrefix}{rgb.r};{rgb.g};{rgb.b}m"
|
||||
|
||||
template ansiForegroundColorCode*(color: static[Color]): string =
|
||||
const rgb = extractRGB(color)
|
||||
(static(fmt"{fgPrefix}{rgb.r};{rgb.g};{rgb.b}m"))
|
||||
|
||||
proc ansiBackgroundColorCode*(color: Color): string =
|
||||
when hasThreadSupport:
|
||||
let rgb = extractRGB(color)
|
||||
result = fmt"{bgPrefix}{rgb.r};{rgb.g};{rgb.b}m"
|
||||
else:
|
||||
if colorsBGCache.hasKey(color):
|
||||
result = colorsBGCache[color]
|
||||
else:
|
||||
let rgb = extractRGB(color)
|
||||
result = fmt"{bgPrefix}{rgb.r};{rgb.g};{rgb.b}m"
|
||||
colorsFGCache[color] = result
|
||||
let rgb = extractRGB(color)
|
||||
result = fmt"{bgPrefix}{rgb.r};{rgb.g};{rgb.b}m"
|
||||
|
||||
template ansiBackgroundColorCode*(color: static[Color]): string =
|
||||
const rgb = extractRGB(color)
|
||||
@@ -665,16 +647,17 @@ template ansiBackgroundColorCode*(color: static[Color]): string =
|
||||
|
||||
proc setForegroundColor*(f: File, color: Color) =
|
||||
## Sets the terminal's foreground true color.
|
||||
if trueColorIsEnabled:
|
||||
if getTerminal().trueColorIsEnabled:
|
||||
f.write(ansiForegroundColorCode(color))
|
||||
|
||||
proc setBackgroundColor*(f: File, color: Color) =
|
||||
## Sets the terminal's background true color.
|
||||
if trueColorIsEnabled:
|
||||
if getTerminal().trueColorIsEnabled:
|
||||
f.write(ansiBackgroundColorCode(color))
|
||||
|
||||
proc setTrueColor(f: File, color: Color) =
|
||||
if fgSetColor:
|
||||
let term = getTerminal()
|
||||
if term.fgSetColor:
|
||||
setForegroundColor(f, color)
|
||||
else:
|
||||
setBackgroundColor(f, color)
|
||||
@@ -726,11 +709,10 @@ macro styledWrite*(f: File, m: varargs[typed]): untyped =
|
||||
## stdout.styledWrite(fgRed, "red text ")
|
||||
## stdout.styledWrite(fgGreen, "green text")
|
||||
##
|
||||
let m = callsite()
|
||||
var reset = false
|
||||
result = newNimNode(nnkStmtList)
|
||||
|
||||
for i in countup(2, m.len - 1):
|
||||
for i in countup(0, m.len - 1):
|
||||
let item = m[i]
|
||||
case item.kind
|
||||
of nnkStrLit..nnkTripleStrLit:
|
||||
@@ -871,56 +853,65 @@ proc resetAttributes*() {.noconv.} =
|
||||
|
||||
proc isTrueColorSupported*(): bool =
|
||||
## Returns true if a terminal supports true color.
|
||||
return trueColorIsSupported
|
||||
return getTerminal().trueColorIsSupported
|
||||
|
||||
when defined(windows):
|
||||
import os
|
||||
|
||||
proc enableTrueColors*() =
|
||||
## Enable true color.
|
||||
var term = getTerminal()
|
||||
when defined(windows):
|
||||
var
|
||||
ver: OSVERSIONINFO
|
||||
ver.dwOSVersionInfoSize = sizeof(ver).DWORD
|
||||
let res = getVersionExW(addr ver)
|
||||
if res == 0:
|
||||
trueColorIsSupported = false
|
||||
term.trueColorIsSupported = false
|
||||
else:
|
||||
trueColorIsSupported = ver.dwMajorVersion > 10 or
|
||||
term.trueColorIsSupported = ver.dwMajorVersion > 10 or
|
||||
(ver.dwMajorVersion == 10 and (ver.dwMinorVersion > 0 or
|
||||
(ver.dwMinorVersion == 0 and ver.dwBuildNumber >= 10586)))
|
||||
if not trueColorIsSupported:
|
||||
trueColorIsSupported = getEnv("ANSICON_DEF").len > 0
|
||||
if not term.trueColorIsSupported:
|
||||
term.trueColorIsSupported = getEnv("ANSICON_DEF").len > 0
|
||||
|
||||
if trueColorIsSupported:
|
||||
if term.trueColorIsSupported:
|
||||
if getEnv("ANSICON_DEF").len == 0:
|
||||
var mode: DWORD = 0
|
||||
if getConsoleMode(getStdHandle(STD_OUTPUT_HANDLE), addr(mode)) != 0:
|
||||
mode = mode or ENABLE_VIRTUAL_TERMINAL_PROCESSING
|
||||
if setConsoleMode(getStdHandle(STD_OUTPUT_HANDLE), mode) != 0:
|
||||
trueColorIsEnabled = true
|
||||
term.trueColorIsEnabled = true
|
||||
else:
|
||||
trueColorIsEnabled = false
|
||||
term.trueColorIsEnabled = false
|
||||
else:
|
||||
trueColorIsEnabled = true
|
||||
term.trueColorIsEnabled = true
|
||||
else:
|
||||
trueColorIsSupported = string(getEnv("COLORTERM")).toLowerAscii() in ["truecolor", "24bit"]
|
||||
trueColorIsEnabled = trueColorIsSupported
|
||||
term.trueColorIsSupported = string(getEnv("COLORTERM")).toLowerAscii() in ["truecolor", "24bit"]
|
||||
term.trueColorIsEnabled = term.trueColorIsSupported
|
||||
|
||||
proc disableTrueColors*() =
|
||||
## Disable true color.
|
||||
var term = getTerminal()
|
||||
when defined(windows):
|
||||
if trueColorIsSupported:
|
||||
if term.trueColorIsSupported:
|
||||
if getEnv("ANSICON_DEF").len == 0:
|
||||
var mode: DWORD = 0
|
||||
if getConsoleMode(getStdHandle(STD_OUTPUT_HANDLE), addr(mode)) != 0:
|
||||
mode = mode and not ENABLE_VIRTUAL_TERMINAL_PROCESSING
|
||||
discard setConsoleMode(getStdHandle(STD_OUTPUT_HANDLE), mode)
|
||||
trueColorIsEnabled = false
|
||||
term.trueColorIsEnabled = false
|
||||
else:
|
||||
trueColorIsEnabled = false
|
||||
term.trueColorIsEnabled = false
|
||||
|
||||
proc newTerminal(): PTerminal =
|
||||
new result
|
||||
when defined(windows):
|
||||
initTerminal(result)
|
||||
|
||||
when not defined(testing) and isMainModule:
|
||||
assert ansiStyleCode(styleBright) == "\e[1m"
|
||||
assert ansiStyleCode(styleStrikethrough) == "\e[9m"
|
||||
#system.addQuitProc(resetAttributes)
|
||||
write(stdout, "never mind")
|
||||
stdout.eraseLine()
|
||||
|
||||
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(int) #=> 9223372036854775807
|
||||
|
||||
proc high*[T: Ordinal](x: typeDesc[T]): T {.magic: "High", noSideEffect.}
|
||||
proc high*[T: Ordinal|enum](x: typeDesc[T]): T {.magic: "High", noSideEffect.}
|
||||
proc high*[T](x: openArray[T]): int {.magic: "High", noSideEffect.}
|
||||
proc high*[I, T](x: array[I, T]): I {.magic: "High", noSideEffect.}
|
||||
proc high*[I, T](x: typeDesc[array[I, T]]): I {.magic: "High", noSideEffect.}
|
||||
proc high*(x: cstring): int {.magic: "High", noSideEffect.}
|
||||
proc high*(x: string): int {.magic: "High", noSideEffect.}
|
||||
|
||||
proc low*[T: Ordinal](x: typeDesc[T]): T {.magic: "Low", noSideEffect.}
|
||||
proc low*[T: Ordinal|enum](x: typeDesc[T]): T {.magic: "Low", noSideEffect.}
|
||||
proc low*[T](x: openArray[T]): int {.magic: "Low", noSideEffect.}
|
||||
proc low*[I, T](x: array[I, T]): I {.magic: "Low", noSideEffect.}
|
||||
proc low*[T](x: T): T {.magic: "Low", noSideEffect.}
|
||||
@@ -806,7 +806,7 @@ proc card*[T](x: set[T]): int {.magic: "Card", noSideEffect.}
|
||||
## var i = {1,2,3,4}
|
||||
## card(i) #=> 4
|
||||
|
||||
proc ord*[T](x: T): int {.magic: "Ord", noSideEffect.}
|
||||
proc ord*[T: Ordinal|enum](x: T): int {.magic: "Ord", noSideEffect.}
|
||||
## returns the internal int value of an ordinal value ``x``.
|
||||
##
|
||||
## .. code-block:: nim
|
||||
@@ -2959,6 +2959,22 @@ when not defined(JS): #and not defined(nimscript):
|
||||
## useful for low-level file access
|
||||
|
||||
include "system/ansi_c"
|
||||
include "system/memory"
|
||||
|
||||
proc zeroMem(p: pointer, size: Natural) =
|
||||
nimZeroMem(p, size)
|
||||
when declared(memTrackerOp):
|
||||
memTrackerOp("zeroMem", p, size)
|
||||
proc copyMem(dest, source: pointer, size: Natural) =
|
||||
nimCopyMem(dest, source, size)
|
||||
when declared(memTrackerOp):
|
||||
memTrackerOp("copyMem", dest, size)
|
||||
proc moveMem(dest, source: pointer, size: Natural) =
|
||||
c_memmove(dest, source, size)
|
||||
when declared(memTrackerOp):
|
||||
memTrackerOp("moveMem", dest, size)
|
||||
proc equalMem(a, b: pointer, size: Natural): bool =
|
||||
nimCmpMem(a, b, size) == 0
|
||||
|
||||
proc cmp(x, y: string): int =
|
||||
when nimvm:
|
||||
@@ -2967,7 +2983,7 @@ when not defined(JS): #and not defined(nimscript):
|
||||
else: result = 0
|
||||
else:
|
||||
let minlen = min(x.len, y.len)
|
||||
result = int(c_memcmp(x.cstring, y.cstring, minlen.csize))
|
||||
result = int(nimCmpMem(x.cstring, y.cstring, minlen.csize))
|
||||
if result == 0:
|
||||
result = x.len - y.len
|
||||
|
||||
@@ -3256,22 +3272,6 @@ when not defined(JS): #and not defined(nimscript):
|
||||
when defined(memtracker):
|
||||
include "system/memtracker"
|
||||
|
||||
when not defined(nimscript):
|
||||
proc zeroMem(p: pointer, size: Natural) =
|
||||
c_memset(p, 0, size)
|
||||
when declared(memTrackerOp):
|
||||
memTrackerOp("zeroMem", p, size)
|
||||
proc copyMem(dest, source: pointer, size: Natural) =
|
||||
c_memcpy(dest, source, size)
|
||||
when declared(memTrackerOp):
|
||||
memTrackerOp("copyMem", dest, size)
|
||||
proc moveMem(dest, source: pointer, size: Natural) =
|
||||
c_memmove(dest, source, size)
|
||||
when declared(memTrackerOp):
|
||||
memTrackerOp("moveMem", dest, size)
|
||||
proc equalMem(a, b: pointer, size: Natural): bool =
|
||||
c_memcmp(a, b, size) == 0
|
||||
|
||||
when hostOS == "standalone":
|
||||
include "system/embedded"
|
||||
else:
|
||||
@@ -4154,11 +4154,10 @@ template doAssertRaises*(exception, code: untyped): typed =
|
||||
## .. code-block:: nim
|
||||
## doAssertRaises(ValueError):
|
||||
## raise newException(ValueError, "Hello World")
|
||||
# TODO: investigate why runnableExamples here caused
|
||||
# https://github.com/nim-lang/Nim/issues/8223
|
||||
var wrong = false
|
||||
try:
|
||||
code
|
||||
if true:
|
||||
code
|
||||
wrong = true
|
||||
except exception:
|
||||
discard
|
||||
|
||||
@@ -830,7 +830,7 @@ proc rawDealloc(a: var MemRegion, p: pointer) =
|
||||
c.freeList = f
|
||||
when overwriteFree:
|
||||
# set to 0xff to check for usage after free bugs:
|
||||
c_memset(cast[pointer](cast[int](p) +% sizeof(FreeCell)), -1'i32,
|
||||
nimSetMem(cast[pointer](cast[int](p) +% sizeof(FreeCell)), -1'i32,
|
||||
s -% sizeof(FreeCell))
|
||||
# check if it is not in the freeSmallChunks[s] list:
|
||||
if c.free < s:
|
||||
@@ -847,7 +847,7 @@ proc rawDealloc(a: var MemRegion, p: pointer) =
|
||||
s == 0, "rawDealloc 2")
|
||||
else:
|
||||
# set to 0xff to check for usage after free bugs:
|
||||
when overwriteFree: c_memset(p, -1'i32, c.size -% bigChunkOverhead())
|
||||
when overwriteFree: nimSetMem(p, -1'i32, c.size -% bigChunkOverhead())
|
||||
# free big chunk
|
||||
var c = cast[PBigChunk](c)
|
||||
dec a.occ, c.size
|
||||
|
||||
@@ -25,6 +25,8 @@ proc c_memset(p: pointer, value: cint, size: csize): pointer {.
|
||||
importc: "memset", header: "<string.h>", discardable.}
|
||||
proc c_strcmp(a, b: cstring): cint {.
|
||||
importc: "strcmp", header: "<string.h>", noSideEffect.}
|
||||
proc c_strlen(a: cstring): csize {.
|
||||
importc: "strlen", header: "<string.h>", noSideEffect.}
|
||||
|
||||
when defined(linux) and defined(amd64):
|
||||
type
|
||||
|
||||
47
lib/system/memory.nim
Normal file
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:
|
||||
# memset to \L so that we can tell how far fgets wrote, even on EOF, where
|
||||
# fgets doesn't append an \L
|
||||
c_memset(addr line.string[pos], '\L'.ord, sp)
|
||||
nimSetMem(addr line.string[pos], '\L'.ord, sp)
|
||||
var fgetsSuccess = c_fgets(addr line.string[pos], sp, f) != nil
|
||||
if not fgetsSuccess: checkErr(f)
|
||||
let m = c_memchr(addr line.string[pos], '\L'.ord, sp)
|
||||
|
||||
@@ -327,6 +327,7 @@ proc ERR_load_BIO_strings*(){.cdecl, dynlib: DLLUtilName, importc.}
|
||||
proc SSL_new*(context: SslCtx): SslPtr{.cdecl, dynlib: DLLSSLName, importc.}
|
||||
proc SSL_free*(ssl: SslPtr){.cdecl, dynlib: DLLSSLName, importc.}
|
||||
proc SSL_get_SSL_CTX*(ssl: SslPtr): SslCtx {.cdecl, dynlib: DLLSSLName, importc.}
|
||||
proc SSL_set_SSL_CTX*(ssl: SslPtr, ctx: SslCtx): SslCtx {.cdecl, dynlib: DLLSSLName, importc.}
|
||||
proc SSL_CTX_new*(meth: PSSL_METHOD): SslCtx{.cdecl,
|
||||
dynlib: DLLSSLName, importc.}
|
||||
proc SSL_CTX_load_verify_locations*(ctx: SslCtx, CAfile: cstring,
|
||||
|
||||
10
tests/casestmt/t8333.nim
Normal file
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:
|
||||
let dt = initDateTime(01, mJan, 2017, 12, 00, 00, utcPlus2)
|
||||
doAssert $dt == "2017-01-01T12:00:00+02:00"
|
||||
doAssert $dt.utc == "2017-01-01T10:00:00+00:00"
|
||||
doAssert $dt.utc == "2017-01-01T10:00:00Z"
|
||||
doAssert $dt.utc.inZone(utcPlus2) == $dt
|
||||
|
||||
doAssert $initDateTime(01, mJan, 1911, 12, 00, 00, utc()) == "1911-01-01T12:00:00+00:00"
|
||||
doAssert $initDateTime(01, mJan, 0023, 12, 00, 00, utc()) == "0023-01-01T12:00:00+00:00"
|
||||
doAssert $initDateTime(01, mJan, 1911, 12, 00, 00, utc()) == "1911-01-01T12:00:00Z"
|
||||
doAssert $initDateTime(01, mJan, 0023, 12, 00, 00, utc()) == "0023-01-01T12:00:00Z"
|
||||
@@ -24,3 +24,23 @@ macro foo: typed =
|
||||
else: echo "Does not compute! (test OK)"
|
||||
|
||||
foo()
|
||||
|
||||
#------------------------------------
|
||||
# bug #8287
|
||||
type MyString = distinct string
|
||||
|
||||
proc `$` (c: MyString): string {.borrow.}
|
||||
|
||||
proc `!!` (c: cstring): int =
|
||||
c.len
|
||||
|
||||
proc f(name: MyString): int =
|
||||
!! $ name
|
||||
|
||||
macro repr_and_parse(fn: typed): typed =
|
||||
let fn_impl = fn.getImpl
|
||||
fn_impl.name = genSym(nskProc, $fn_impl.name)
|
||||
echo fn_impl.repr
|
||||
result = parseStmt(fn_impl.repr)
|
||||
|
||||
repr_and_parse(f)
|
||||
@@ -9,7 +9,7 @@ proc printfImpl(formatstr: cstring) {.importc: "printf", varargs.}
|
||||
|
||||
iterator tokenize(format: string): char =
|
||||
var i = 0
|
||||
while true:
|
||||
while i < format.len:
|
||||
case format[i]
|
||||
of '%':
|
||||
case format[i+1]
|
||||
@@ -42,7 +42,8 @@ macro printf(formatString: string{lit}, args: varargs[typed]): untyped =
|
||||
$expectedType & ", actual type: " & $actualType
|
||||
|
||||
# keep the original callsite, but use cprintf instead
|
||||
result = callsite()
|
||||
result[0] = bindSym"printfImpl"
|
||||
result = newCall(bindSym"printfImpl")
|
||||
result.add formatString
|
||||
for a in args: result.add a
|
||||
|
||||
printf("test %d\n", 10)
|
||||
|
||||
@@ -5,4 +5,7 @@ discard """
|
||||
# Tests that module names can contain multi byte characters
|
||||
|
||||
let a = 1
|
||||
doAssert åäö.a == 1
|
||||
doAssert åäö.a == 1
|
||||
|
||||
proc inlined() {.inline.} = discard
|
||||
inlined()
|
||||
@@ -145,4 +145,12 @@ block:
|
||||
type Annotated {.simpleAttr.} = object
|
||||
|
||||
proc generic_proc[T]() =
|
||||
assert Annotated.hasCustomPragma(simpleAttr)
|
||||
assert Annotated.hasCustomPragma(simpleAttr)
|
||||
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
# Pragma on proc type
|
||||
|
||||
let a: proc(x: int) {.defaultValue(5).} = nil
|
||||
static:
|
||||
doAssert hasCustomPragma(a.type, defaultValue)
|
||||
|
||||
@@ -8,6 +8,24 @@ discard """
|
||||
import
|
||||
times, os, strutils, unittest
|
||||
|
||||
proc staticTz(hours, minutes, seconds: int = 0): Timezone {.noSideEffect.} =
|
||||
let offset = hours * 3600 + minutes * 60 + seconds
|
||||
|
||||
proc zoneInfoFromTz(adjTime: Time): ZonedTime {.locks: 0.} =
|
||||
result.isDst = false
|
||||
result.utcOffset = offset
|
||||
result.adjTime = adjTime
|
||||
|
||||
proc zoneInfoFromUtc(time: Time): ZonedTime {.locks: 0.}=
|
||||
result.isDst = false
|
||||
result.utcOffset = offset
|
||||
result.adjTime = fromUnix(time.toUnix - offset)
|
||||
|
||||
result.name = ""
|
||||
result.zoneInfoFromTz = zoneInfoFromTz
|
||||
result.zoneInfoFromUtc = zoneInfoFromUtc
|
||||
|
||||
|
||||
# $ date --date='@2147483647'
|
||||
# Tue 19 Jan 03:14:07 GMT 2038
|
||||
|
||||
@@ -19,25 +37,10 @@ proc checkFormat(t: DateTime, format, expected: string) =
|
||||
echo "actual : ", actual
|
||||
doAssert false
|
||||
|
||||
let t = fromUnix(2147483647).utc
|
||||
t.checkFormat("ddd dd MMM hh:mm:ss yyyy", "Tue 19 Jan 03:14:07 2038")
|
||||
t.checkFormat("ddd ddMMMhh:mm:ssyyyy", "Tue 19Jan03:14:072038")
|
||||
t.checkFormat("d dd ddd dddd h hh H HH m mm M MM MMM MMMM s" &
|
||||
" ss t tt y yy yyy yyyy yyyyy z zz zzz",
|
||||
"19 19 Tue Tuesday 3 03 3 03 14 14 1 01 Jan January 7 07 A AM 8 38 038 2038 02038 +0 +00 +00:00")
|
||||
|
||||
t.checkFormat("yyyyMMddhhmmss", "20380119031407")
|
||||
|
||||
# issue 7620
|
||||
let t7620_am = parse("4/15/2017 12:01:02 AM +0", "M/d/yyyy' 'h:mm:ss' 'tt' 'z", utc())
|
||||
t7620_am.checkFormat("M/d/yyyy' 'h:mm:ss' 'tt' 'z", "4/15/2017 12:01:02 AM +0")
|
||||
let t7620_pm = parse("4/15/2017 12:01:02 PM +0", "M/d/yyyy' 'h:mm:ss' 'tt' 'z", utc())
|
||||
t7620_pm.checkFormat("M/d/yyyy' 'h:mm:ss' 'tt' 'z", "4/15/2017 12:01:02 PM +0")
|
||||
|
||||
let t2 = fromUnix(160070789).utc # Mon 27 Jan 16:06:29 GMT 1975
|
||||
let t2 = fromUnix(160070789).utc() # Mon 27 Jan 16:06:29 GMT 1975
|
||||
t2.checkFormat("d dd ddd dddd h hh H HH m mm M MM MMM MMMM s" &
|
||||
" ss t tt y yy yyy yyyy yyyyy z zz zzz",
|
||||
"27 27 Mon Monday 4 04 16 16 6 06 1 01 Jan January 29 29 P PM 5 75 975 1975 01975 +0 +00 +00:00")
|
||||
"27 27 Mon Monday 4 04 16 16 6 06 1 01 Jan January 29 29 P PM 5 75 975 1975 01975 Z Z Z")
|
||||
|
||||
var t4 = fromUnix(876124714).utc # Mon 6 Oct 08:58:34 BST 1997
|
||||
t4.checkFormat("M MM MMM MMMM", "10 10 Oct October")
|
||||
@@ -83,16 +86,16 @@ let seqB: seq[Time] = @[]
|
||||
doAssert seqA == seqB
|
||||
|
||||
for tz in [
|
||||
(0, "+0", "+00", "+00:00"), # UTC
|
||||
(-3600, "+1", "+01", "+01:00"), # CET
|
||||
(-39600, "+11", "+11", "+11:00"), # two digits
|
||||
(-1800, "+0", "+00", "+00:30"), # half an hour
|
||||
(7200, "-2", "-02", "-02:00"), # positive
|
||||
(38700, "-10", "-10", "-10:45")]: # positive with three quaters hour
|
||||
let ti = DateTime(month: mJan, monthday: 1, utcOffset: tz[0])
|
||||
doAssert ti.format("z") == tz[1]
|
||||
doAssert ti.format("zz") == tz[2]
|
||||
doAssert ti.format("zzz") == tz[3]
|
||||
(staticTz(seconds = 0), "+0", "+00", "+00:00"), # UTC
|
||||
(staticTz(seconds = -3600), "+1", "+01", "+01:00"), # CET
|
||||
(staticTz(seconds = -39600), "+11", "+11", "+11:00"), # two digits
|
||||
(staticTz(seconds = -1800), "+0", "+00", "+00:30"), # half an hour
|
||||
(staticTz(seconds = 7200), "-2", "-02", "-02:00"), # positive
|
||||
(staticTz(seconds = 38700), "-10", "-10", "-10:45")]: # positive with three quaters hour
|
||||
let dt = initDateTime(1, mJan, 2000, 00, 00, 00, tz[0])
|
||||
doAssert dt.format("z") == tz[1]
|
||||
doAssert dt.format("zz") == tz[2]
|
||||
doAssert dt.format("zzz") == tz[3]
|
||||
|
||||
block countLeapYears:
|
||||
# 1920, 2004 and 2020 are leap years, and should be counted starting at the following year
|
||||
@@ -112,11 +115,9 @@ template parseTest(s, f, sExpected: string, ydExpected: int) =
|
||||
let
|
||||
parsed = s.parse(f, utc())
|
||||
parsedStr = $parsed
|
||||
if parsedStr != sExpected:
|
||||
echo "GOT ", parsedStr, " EXPECTED ", sExpected, " FORMAT ", f
|
||||
check parsedStr == sExpected
|
||||
if parsed.yearday != ydExpected:
|
||||
echo s
|
||||
echo parsed.repr
|
||||
echo parsed.yearday, " exp: ", ydExpected
|
||||
check(parsed.yearday == ydExpected)
|
||||
|
||||
template parseTestExcp(s, f: string) =
|
||||
@@ -130,51 +131,43 @@ template parseTestTimeOnly(s, f, sExpected: string) =
|
||||
# explicit timezone offsets in all tests.
|
||||
template runTimezoneTests() =
|
||||
parseTest("Tuesday at 09:04am on Dec 15, 2015 +0",
|
||||
"dddd at hh:mmtt on MMM d, yyyy z", "2015-12-15T09:04:00+00:00", 348)
|
||||
"dddd 'at' hh:mmtt 'on' MMM d, yyyy z", "2015-12-15T09:04:00Z", 348)
|
||||
# ANSIC = "Mon Jan _2 15:04:05 2006"
|
||||
parseTest("Thu Jan 12 15:04:05 2006 +0", "ddd MMM dd HH:mm:ss yyyy z",
|
||||
"2006-01-12T15:04:05+00:00", 11)
|
||||
"2006-01-12T15:04:05Z", 11)
|
||||
# UnixDate = "Mon Jan _2 15:04:05 MST 2006"
|
||||
parseTest("Thu Jan 12 15:04:05 2006 +0", "ddd MMM dd HH:mm:ss yyyy z",
|
||||
"2006-01-12T15:04:05+00:00", 11)
|
||||
"2006-01-12T15:04:05Z", 11)
|
||||
# RubyDate = "Mon Jan 02 15:04:05 -0700 2006"
|
||||
parseTest("Mon Feb 29 15:04:05 -07:00 2016 +0", "ddd MMM dd HH:mm:ss zzz yyyy z",
|
||||
"2016-02-29T15:04:05+00:00", 59) # leap day
|
||||
"2016-02-29T15:04:05Z", 59) # leap day
|
||||
# RFC822 = "02 Jan 06 15:04 MST"
|
||||
parseTest("12 Jan 16 15:04 +0", "dd MMM yy HH:mm z",
|
||||
"2016-01-12T15:04:00+00:00", 11)
|
||||
"2016-01-12T15:04:00Z", 11)
|
||||
# RFC822Z = "02 Jan 06 15:04 -0700" # RFC822 with numeric zone
|
||||
parseTest("01 Mar 16 15:04 -07:00", "dd MMM yy HH:mm zzz",
|
||||
"2016-03-01T22:04:00+00:00", 60) # day after february in leap year
|
||||
"2016-03-01T22:04:00Z", 60) # day after february in leap year
|
||||
# RFC850 = "Monday, 02-Jan-06 15:04:05 MST"
|
||||
parseTest("Monday, 12-Jan-06 15:04:05 +0", "dddd, dd-MMM-yy HH:mm:ss z",
|
||||
"2006-01-12T15:04:05+00:00", 11)
|
||||
"2006-01-12T15:04:05Z", 11)
|
||||
# RFC1123 = "Mon, 02 Jan 2006 15:04:05 MST"
|
||||
parseTest("Sun, 01 Mar 2015 15:04:05 +0", "ddd, dd MMM yyyy HH:mm:ss z",
|
||||
"2015-03-01T15:04:05+00:00", 59) # day after february in non-leap year
|
||||
"2015-03-01T15:04:05Z", 59) # day after february in non-leap year
|
||||
# RFC1123Z = "Mon, 02 Jan 2006 15:04:05 -0700" # RFC1123 with numeric zone
|
||||
parseTest("Thu, 12 Jan 2006 15:04:05 -07:00", "ddd, dd MMM yyyy HH:mm:ss zzz",
|
||||
"2006-01-12T22:04:05+00:00", 11)
|
||||
"2006-01-12T22:04:05Z", 11)
|
||||
# RFC3339 = "2006-01-02T15:04:05Z07:00"
|
||||
parseTest("2006-01-12T15:04:05Z-07:00", "yyyy-MM-ddTHH:mm:ssZzzz",
|
||||
"2006-01-12T22:04:05+00:00", 11)
|
||||
parseTest("2006-01-12T15:04:05Z-07:00", "yyyy-MM-dd'T'HH:mm:ss'Z'zzz",
|
||||
"2006-01-12T22:04:05+00:00", 11)
|
||||
"2006-01-12T22:04:05Z", 11)
|
||||
# RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00"
|
||||
parseTest("2006-01-12T15:04:05.999999999Z-07:00",
|
||||
"yyyy-MM-ddTHH:mm:ss.999999999Zzzz", "2006-01-12T22:04:05+00:00", 11)
|
||||
"yyyy-MM-dd'T'HH:mm:ss'.999999999Z'zzz", "2006-01-12T22:04:05Z", 11)
|
||||
for tzFormat in ["z", "zz", "zzz"]:
|
||||
# formatting timezone as 'Z' for UTC
|
||||
parseTest("2001-01-12T22:04:05Z", "yyyy-MM-dd'T'HH:mm:ss" & tzFormat,
|
||||
"2001-01-12T22:04:05+00:00", 11)
|
||||
"2001-01-12T22:04:05Z", 11)
|
||||
# Kitchen = "3:04PM"
|
||||
parseTestTimeOnly("3:04PM", "h:mmtt", "15:04:00")
|
||||
#when not defined(testing):
|
||||
# echo "Kitchen: " & $s.parse(f)
|
||||
# var ti = timeToTimeInfo(getTime())
|
||||
# echo "Todays date after decoding: ", ti
|
||||
# var tint = timeToTimeInterval(getTime())
|
||||
# echo "Todays date after decoding to interval: ", tint
|
||||
|
||||
# Bug with parse not setting DST properly if the current local DST differs from
|
||||
# the date being parsed. Need to test parse dates both in and out of DST. We
|
||||
@@ -195,8 +188,8 @@ template runTimezoneTests() =
|
||||
let
|
||||
parsedJan = parse("2016-01-05 04:00:00+01:00", "yyyy-MM-dd HH:mm:sszzz")
|
||||
parsedJul = parse("2016-07-01 04:00:00+01:00", "yyyy-MM-dd HH:mm:sszzz")
|
||||
doAssert toTime(parsedJan).toUnix == 1451962800
|
||||
doAssert toTime(parsedJul).toUnix == 1467342000
|
||||
check toTime(parsedJan).toUnix == 1451962800
|
||||
check toTime(parsedJul).toUnix == 1467342000
|
||||
|
||||
suite "ttimes":
|
||||
|
||||
@@ -256,7 +249,7 @@ suite "ttimes":
|
||||
check $(dt + initDuration(days = 1)) == "2017-03-26T13:00:00+02:00"
|
||||
|
||||
test "datetime before epoch":
|
||||
check $fromUnix(-2147483648).utc == "1901-12-13T20:45:52+00:00"
|
||||
check $fromUnix(-2147483648).utc == "1901-12-13T20:45:52Z"
|
||||
|
||||
test "adding/subtracting time across dst":
|
||||
putenv("TZ", "Europe/Stockholm")
|
||||
@@ -319,6 +312,15 @@ suite "ttimes":
|
||||
test "incorrect inputs: timezone (zzz) 3":
|
||||
parseTestExcp("2018-02-19 16:30:00 +01:0", "yyyy-MM-dd hh:mm:ss zzz")
|
||||
|
||||
test "incorrect inputs: year (yyyy/uuuu)":
|
||||
parseTestExcp("-0001", "yyyy")
|
||||
parseTestExcp("-0001", "YYYY")
|
||||
parseTestExcp("1", "yyyy")
|
||||
parseTestExcp("12345", "yyyy")
|
||||
parseTestExcp("1", "uuuu")
|
||||
parseTestExcp("12345", "uuuu")
|
||||
parseTestExcp("-1 BC", "UUUU g")
|
||||
|
||||
test "dynamic timezone":
|
||||
proc staticOffset(offset: int): Timezone =
|
||||
proc zoneInfoFromTz(adjTime: Time): ZonedTime =
|
||||
@@ -340,7 +342,7 @@ suite "ttimes":
|
||||
check dt.utcOffset == -9000
|
||||
check dt.isDst == false
|
||||
check $dt == "2000-01-01T12:00:00+02:30"
|
||||
check $dt.utc == "2000-01-01T09:30:00+00:00"
|
||||
check $dt.utc == "2000-01-01T09:30:00Z"
|
||||
check $dt.utc.inZone(tz) == $dt
|
||||
|
||||
test "isLeapYear":
|
||||
@@ -351,12 +353,12 @@ suite "ttimes":
|
||||
|
||||
test "subtract months":
|
||||
var dt = initDateTime(1, mFeb, 2017, 00, 00, 00, utc())
|
||||
check $(dt - initTimeInterval(months = 1)) == "2017-01-01T00:00:00+00:00"
|
||||
check $(dt - initTimeInterval(months = 1)) == "2017-01-01T00:00:00Z"
|
||||
dt = initDateTime(15, mMar, 2017, 00, 00, 00, utc())
|
||||
check $(dt - initTimeInterval(months = 1)) == "2017-02-15T00:00:00+00:00"
|
||||
check $(dt - initTimeInterval(months = 1)) == "2017-02-15T00:00:00Z"
|
||||
dt = initDateTime(31, mMar, 2017, 00, 00, 00, utc())
|
||||
# This happens due to monthday overflow. It's consistent with Phobos.
|
||||
check $(dt - initTimeInterval(months = 1)) == "2017-03-03T00:00:00+00:00"
|
||||
check $(dt - initTimeInterval(months = 1)) == "2017-03-03T00:00:00Z"
|
||||
|
||||
test "duration":
|
||||
let d = initDuration
|
||||
@@ -384,11 +386,11 @@ suite "ttimes":
|
||||
discard initDateTime(1, mJan, -35_000, 12, 00, 00)
|
||||
discard initDateTime(1, mJan, 35_000, 12, 00, 00)
|
||||
# with duration/timeinterval
|
||||
let dt = initDateTime(1, mJan, 35_000, 12, 00, 00, utc()) +
|
||||
let dt = initDateTime(1, mJan, -35_000, 12, 00, 00, utc()) +
|
||||
initDuration(seconds = 1)
|
||||
check dt.second == 1
|
||||
let dt2 = dt + 35_001.years
|
||||
check $dt2 == "0001-01-01T12:00:01+00:00"
|
||||
check $dt2 == "0001-01-01T12:00:01Z"
|
||||
|
||||
test "compare datetimes":
|
||||
var dt1 = now()
|
||||
@@ -426,4 +428,90 @@ suite "ttimes":
|
||||
check (-1).fromWinTime.nanosecond == convert(Seconds, Nanoseconds, 1) - 100
|
||||
check -1.fromWinTime.toWinTime == -1
|
||||
# One nanosecond is discarded due to differences in time resolution
|
||||
check initTime(0, 101).toWinTime.fromWinTime.nanosecond == 100
|
||||
check initTime(0, 101).toWinTime.fromWinTime.nanosecond == 100
|
||||
check initTime(0, 101).toWinTime.fromWinTime.nanosecond == 100
|
||||
|
||||
test "issue 7620":
|
||||
let layout = "M/d/yyyy' 'h:mm:ss' 'tt' 'z"
|
||||
let t7620_am = parse("4/15/2017 12:01:02 AM +0", layout, utc())
|
||||
check t7620_am.format(layout) == "4/15/2017 12:01:02 AM Z"
|
||||
let t7620_pm = parse("4/15/2017 12:01:02 PM +0", layout, utc())
|
||||
check t7620_pm.format(layout) == "4/15/2017 12:01:02 PM Z"
|
||||
|
||||
test "format":
|
||||
var dt = initDateTime(1, mJan, -0001,
|
||||
17, 01, 02, 123_456_789,
|
||||
staticTz(hours = 1, minutes = 2, seconds = 3))
|
||||
check dt.format("d") == "1"
|
||||
check dt.format("dd") == "01"
|
||||
check dt.format("ddd") == "Fri"
|
||||
check dt.format("dddd") == "Friday"
|
||||
check dt.format("h") == "5"
|
||||
check dt.format("hh") == "05"
|
||||
check dt.format("H") == "17"
|
||||
check dt.format("HH") == "17"
|
||||
check dt.format("m") == "1"
|
||||
check dt.format("mm") == "01"
|
||||
check dt.format("M") == "1"
|
||||
check dt.format("MM") == "01"
|
||||
check dt.format("MMM") == "Jan"
|
||||
check dt.format("MMMM") == "January"
|
||||
check dt.format("s") == "2"
|
||||
check dt.format("ss") == "02"
|
||||
check dt.format("t") == "P"
|
||||
check dt.format("tt") == "PM"
|
||||
check dt.format("yy") == "02"
|
||||
check dt.format("yyyy") == "0002"
|
||||
check dt.format("YYYY") == "2"
|
||||
check dt.format("uuuu") == "-0001"
|
||||
check dt.format("UUUU") == "-1"
|
||||
check dt.format("z") == "-1"
|
||||
check dt.format("zz") == "-01"
|
||||
check dt.format("zzz") == "-01:02"
|
||||
check dt.format("zzzz") == "-01:02:03"
|
||||
check dt.format("g") == "BC"
|
||||
|
||||
check dt.format("fff") == "123"
|
||||
check dt.format("ffffff") == "123456"
|
||||
check dt.format("fffffffff") == "123456789"
|
||||
dt.nanosecond = 1
|
||||
check dt.format("fff") == "000"
|
||||
check dt.format("ffffff") == "000000"
|
||||
check dt.format("fffffffff") == "000000001"
|
||||
|
||||
dt.year = 12345
|
||||
check dt.format("yyyy") == "+12345"
|
||||
check dt.format("uuuu") == "+12345"
|
||||
dt.year = -12345
|
||||
check dt.format("yyyy") == "+12346"
|
||||
check dt.format("uuuu") == "-12345"
|
||||
|
||||
expect ValueError:
|
||||
discard initTimeFormat("'")
|
||||
|
||||
expect ValueError:
|
||||
discard initTimeFormat("'foo")
|
||||
|
||||
expect ValueError:
|
||||
discard initTimeFormat("foo'")
|
||||
|
||||
test "parse":
|
||||
check $parse("20180101", "yyyyMMdd", utc()) == "2018-01-01T00:00:00Z"
|
||||
parseTestExcp("+120180101", "yyyyMMdd")
|
||||
|
||||
check parse("1", "YYYY", utc()).year == 1
|
||||
check parse("1 BC", "YYYY g", utc()).year == 0
|
||||
check parse("0001 BC", "yyyy g", utc()).year == 0
|
||||
check parse("+12345 BC", "yyyy g", utc()).year == -12344
|
||||
check parse("1 AD", "YYYY g", utc()).year == 1
|
||||
check parse("0001 AD", "yyyy g", utc()).year == 1
|
||||
check parse("+12345 AD", "yyyy g", utc()).year == 12345
|
||||
|
||||
check parse("-1", "UUUU", utc()).year == -1
|
||||
check parse("-0001", "uuuu", utc()).year == -1
|
||||
|
||||
discard parse("foobar", "'foobar'")
|
||||
discard parse("foo'bar", "'foo''''bar'")
|
||||
discard parse("'", "''")
|
||||
|
||||
parseTestExcp("2000 A", "yyyy g")
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
discard """
|
||||
output:""
|
||||
output: ""
|
||||
"""
|
||||
|
||||
doAssert "@[23, 45]" == $(@[23, 45])
|
||||
doAssert "[32, 45]" == $([32, 45])
|
||||
doAssert """@["", "foo", "bar"]""" == $(@["", "foo", "bar"])
|
||||
doAssert """["", "foo", "bar"]""" == $(["", "foo", "bar"])
|
||||
doAssert """["", "foo", "bar"]""" == $(@["", "foo", "bar"].toOpenArray(0, 2))
|
||||
doAssert """["", "foo", "bar"]""" == $(["", "foo", "bar"])
|
||||
doAssert """["", "foo", "bar"]""" == $(@["", "foo", "bar"].toOpenArray(0, 2))
|
||||
|
||||
# bug #2395
|
||||
let alphaSet: set[char] = {'a'..'c'}
|
||||
@@ -69,13 +69,13 @@ var yy: string
|
||||
doAssert xx == @[]
|
||||
doAssert yy == ""
|
||||
|
||||
proc bar(arg: cstring): void =
|
||||
proc bar(arg: cstring) =
|
||||
doAssert arg[0] == '\0'
|
||||
|
||||
proc baz(arg: openarray[char]): void =
|
||||
proc baz(arg: openarray[char]) =
|
||||
doAssert arg.len == 0
|
||||
|
||||
proc stringCompare(): void =
|
||||
proc stringCompare() =
|
||||
var a,b,c,d,e,f,g: string
|
||||
a.add 'a'
|
||||
doAssert a == "a"
|
||||
@@ -102,9 +102,12 @@ proc stringCompare(): void =
|
||||
doAssert "" != "\0\0\0\0\0\0\0\0\0\0"
|
||||
|
||||
var nilstring: string
|
||||
bar(nilstring)
|
||||
#bar(nilstring)
|
||||
baz(nilstring)
|
||||
|
||||
stringCompare()
|
||||
var nilstring: string
|
||||
bar(nilstring)
|
||||
|
||||
static:
|
||||
stringCompare()
|
||||
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')])
|
||||
Reference in New Issue
Block a user