mirror of
https://github.com/nim-lang/Nim.git
synced 2026-09-05 13:10:50 +00:00
Merge branch 'devel' into IOSelector_unregister_fix
This commit is contained in:
16
build_all.bat
Normal file
16
build_all.bat
Normal file
@@ -0,0 +1,16 @@
|
||||
@echo off
|
||||
rem build development version of the compiler; can be rerun safely
|
||||
if not exist csources (
|
||||
git clone --depth 1 https://github.com/nim-lang/csources.git
|
||||
)
|
||||
if not exist bin\nim.exe (
|
||||
cd csources
|
||||
if PROCESSOR_ARCHITECTURE == AMD64 (
|
||||
SET ARCH=64
|
||||
)
|
||||
CALL build.bat
|
||||
cd ..
|
||||
)
|
||||
bin\nim.exe c --skipUserCfg --skipParentCfg koch
|
||||
koch.exe boot -d:release
|
||||
koch.exe tools
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
### Breaking changes in the standard library
|
||||
|
||||
- `base64.encode` no longer supports `lineLen` and `newLine` use `base64.encodeMIME` instead.
|
||||
|
||||
|
||||
### Breaking changes in the compiler
|
||||
@@ -24,6 +25,8 @@
|
||||
|
||||
- `asyncdispatch.drain` now properly takes into account `selector.hasPendingOperations` and only returns once all pending async operations are guaranteed to have completed.
|
||||
- `asyncdispatch.drain` now consistently uses the passed timeout value for all iterations of the event loop, and not just the first iteration. This is more consistent with the other asyncdispatch apis, and allows `asyncdispatch.drain` to be more efficient.
|
||||
- `base64.encode` and `base64.decode` was made faster by about 50%.
|
||||
- `htmlgen` adds [MathML](https://wikipedia.org/wiki/MathML) support (ISO 40314).
|
||||
|
||||
## Language additions
|
||||
|
||||
@@ -31,6 +34,7 @@
|
||||
|
||||
## Language changes
|
||||
|
||||
- Unsigned integer operators have been fixed to allow promotion of the first operand.
|
||||
|
||||
|
||||
### Tool changes
|
||||
|
||||
@@ -1810,11 +1810,11 @@ template getBody*(s: PSym): PNode = s.ast[bodyPos]
|
||||
template detailedInfo*(sym: PSym): string =
|
||||
sym.name.s
|
||||
|
||||
proc isInlineIterator*(s: PSym): bool {.inline.} =
|
||||
s.kind == skIterator and s.typ.callConv != ccClosure
|
||||
proc isInlineIterator*(typ: PType): bool {.inline.} =
|
||||
typ.kind == tyProc and tfIterator in typ.flags and typ.callConv != ccClosure
|
||||
|
||||
proc isClosureIterator*(s: PSym): bool {.inline.} =
|
||||
s.kind == skIterator and s.typ.callConv == ccClosure
|
||||
proc isClosureIterator*(typ: PType): bool {.inline.} =
|
||||
typ.kind == tyProc and tfIterator in typ.flags and typ.callConv == ccClosure
|
||||
|
||||
proc isSinkParam*(s: PSym): bool {.inline.} =
|
||||
s.kind == skParam and (s.typ.kind == tySink or tfHasOwned in s.typ.flags)
|
||||
|
||||
@@ -79,7 +79,7 @@ proc genLiteral(p: BProc, n: PNode, ty: PType): Rope =
|
||||
# with the new semantics for 'nil' strings, we can map "" to nil and
|
||||
# save tons of allocations:
|
||||
if n.strVal.len == 0 and optNilSeqs notin p.options and
|
||||
p.config.selectedGC != gcDestructors:
|
||||
optSeqDestructors notin p.config.globalOptions:
|
||||
result = genNilStringLiteral(p.module, n.info)
|
||||
else:
|
||||
result = genStringLiteral(p.module, n)
|
||||
@@ -250,7 +250,7 @@ proc genGenericAsgn(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
|
||||
# tfShallow flag for the built-in string type too! So we check only
|
||||
# here for this flag, where it is reasonably safe to do so
|
||||
# (for objects, etc.):
|
||||
if p.config.selectedGC == gcDestructors:
|
||||
if optSeqDestructors in p.config.globalOptions:
|
||||
linefmt(p, cpsStmts,
|
||||
"$1 = $2;$n",
|
||||
[rdLoc(dest), rdLoc(src)])
|
||||
@@ -279,7 +279,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
|
||||
of tyRef:
|
||||
genRefAssign(p, dest, src)
|
||||
of tySequence:
|
||||
if p.config.selectedGC == gcDestructors:
|
||||
if optSeqDestructors in p.config.globalOptions:
|
||||
genGenericAsgn(p, dest, src, flags)
|
||||
elif (needToCopy notin flags and src.storage != OnStatic) or canMove(p, src.lode, dest):
|
||||
genRefAssign(p, dest, src)
|
||||
@@ -288,7 +288,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
|
||||
[addrLoc(p.config, dest), rdLoc(src),
|
||||
genTypeInfo(p.module, dest.t, dest.lode.info)])
|
||||
of tyString:
|
||||
if p.config.selectedGC == gcDestructors:
|
||||
if optSeqDestructors in p.config.globalOptions:
|
||||
genGenericAsgn(p, dest, src, flags)
|
||||
elif (needToCopy notin flags and src.storage != OnStatic) or canMove(p, src.lode, dest):
|
||||
genRefAssign(p, dest, src)
|
||||
@@ -934,7 +934,7 @@ proc genSeqElem(p: BProc, n, x, y: PNode, d: var TLoc) =
|
||||
a.r = ropecg(p.module, "(*$1)", [a.r])
|
||||
|
||||
if lfPrepareForMutation in d.flags and ty.kind == tyString and
|
||||
p.config.selectedGC == gcDestructors:
|
||||
optSeqDestructors in p.config.globalOptions:
|
||||
linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)])
|
||||
putIntoDest(p, d, n,
|
||||
ropecg(p.module, "$1$3[$2]", [rdLoc(a), rdCharLoc(b), dataField(p)]), a.storage)
|
||||
@@ -1060,7 +1060,7 @@ proc gcUsage(conf: ConfigRef; n: PNode) =
|
||||
if conf.selectedGC == gcNone: message(conf, n.info, warnGcMem, n.renderTree)
|
||||
|
||||
proc strLoc(p: BProc; d: TLoc): Rope =
|
||||
if p.config.selectedGC == gcDestructors:
|
||||
if optSeqDestructors in p.config.globalOptions:
|
||||
result = byRefLoc(p, d)
|
||||
else:
|
||||
result = rdLoc(d)
|
||||
@@ -1141,7 +1141,7 @@ proc genStrAppend(p: BProc, e: PNode, d: var TLoc) =
|
||||
add(lens, " + ")
|
||||
add(appends, ropecg(p.module, "#appendString($1, $2);$n",
|
||||
[strLoc(p, dest), rdLoc(a)]))
|
||||
if p.config.selectedGC == gcDestructors:
|
||||
if optSeqDestructors in p.config.globalOptions:
|
||||
linefmt(p, cpsStmts, "#prepareAdd($1, $2$3);$n",
|
||||
[byRefLoc(p, dest), lens, L])
|
||||
else:
|
||||
@@ -1206,7 +1206,7 @@ proc rawGenNew(p: BProc, a: TLoc, sizeExpr: Rope) =
|
||||
if sizeExpr.isNil:
|
||||
sizeExpr = "sizeof($1)" % [getTypeDesc(p.module, bt)]
|
||||
|
||||
if optNimV2 in p.config.globalOptions:
|
||||
if optOwnedRefs in p.config.globalOptions:
|
||||
b.r = ropecg(p.module, "($1) #nimNewObj($2)",
|
||||
[getTypeDesc(p.module, typ), sizeExpr])
|
||||
genAssignment(p, a, b, {})
|
||||
@@ -1288,7 +1288,7 @@ proc genNewSeq(p: BProc, e: PNode) =
|
||||
var a, b: TLoc
|
||||
initLocExpr(p, e.sons[1], a)
|
||||
initLocExpr(p, e.sons[2], b)
|
||||
if p.config.selectedGC == gcDestructors:
|
||||
if optSeqDestructors in p.config.globalOptions:
|
||||
let seqtype = skipTypes(e.sons[1].typ, abstractVarRange)
|
||||
linefmt(p, cpsStmts, "$1.len = $2; $1.p = ($4*) #newSeqPayload($2, sizeof($3));$n",
|
||||
[a.rdLoc, b.rdLoc, getTypeDesc(p.module, seqtype.lastSon),
|
||||
@@ -1303,7 +1303,7 @@ proc genNewSeqOfCap(p: BProc; e: PNode; d: var TLoc) =
|
||||
let seqtype = skipTypes(e.typ, abstractVarRange)
|
||||
var a: TLoc
|
||||
initLocExpr(p, e.sons[1], a)
|
||||
if p.config.selectedGC == gcDestructors:
|
||||
if optSeqDestructors in p.config.globalOptions:
|
||||
if d.k == locNone: getTemp(p, e.typ, d, needsInit=false)
|
||||
linefmt(p, cpsStmts, "$1.len = 0; $1.p = ($4*) #newSeqPayload($2, sizeof($3));$n",
|
||||
[d.rdLoc, a.rdLoc, getTypeDesc(p.module, seqtype.lastSon),
|
||||
@@ -1403,7 +1403,7 @@ proc genSeqConstr(p: BProc, n: PNode, d: var TLoc) =
|
||||
getTemp(p, n.typ, d)
|
||||
|
||||
let l = intLiteral(len(n))
|
||||
if p.config.selectedGC == gcDestructors:
|
||||
if optSeqDestructors in p.config.globalOptions:
|
||||
let seqtype = n.typ
|
||||
linefmt(p, cpsStmts, "$1.len = $2; $1.p = ($4*) #newSeqPayload($2, sizeof($3));$n",
|
||||
[rdLoc dest[], l, getTypeDesc(p.module, seqtype.lastSon),
|
||||
@@ -1434,7 +1434,7 @@ proc genArrToSeq(p: BProc, n: PNode, d: var TLoc) =
|
||||
getTemp(p, n.typ, d)
|
||||
# generate call to newSeq before adding the elements per hand:
|
||||
let L = toInt(lengthOrd(p.config, n.sons[1].typ))
|
||||
if p.config.selectedGC == gcDestructors:
|
||||
if optSeqDestructors in p.config.globalOptions:
|
||||
let seqtype = n.typ
|
||||
linefmt(p, cpsStmts, "$1.len = $2; $1.p = ($4*) #newSeqPayload($2, sizeof($3));$n",
|
||||
[rdLoc d, L, getTypeDesc(p.module, seqtype.lastSon),
|
||||
@@ -1484,7 +1484,7 @@ proc genNewFinalize(p: BProc, e: PNode) =
|
||||
gcUsage(p.config, e)
|
||||
|
||||
proc genOfHelper(p: BProc; dest: PType; a: Rope; info: TLineInfo): Rope =
|
||||
if optNimV2 in p.config.globalOptions:
|
||||
if optTinyRtti in p.config.globalOptions:
|
||||
result = ropecg(p.module, "#isObj($1.m_type, $2)",
|
||||
[a, genTypeInfo2Name(p.module, dest)])
|
||||
else:
|
||||
@@ -1535,7 +1535,7 @@ proc genOf(p: BProc, n: PNode, d: var TLoc) =
|
||||
genOf(p, n.sons[1], n.sons[2].typ, d)
|
||||
|
||||
proc genRepr(p: BProc, e: PNode, d: var TLoc) =
|
||||
if optNimV2 in p.config.globalOptions:
|
||||
if optTinyRtti in p.config.globalOptions:
|
||||
localError(p.config, e.info, "'repr' is not available for --newruntime")
|
||||
var a: TLoc
|
||||
initLocExpr(p, e.sons[1], a)
|
||||
@@ -1654,7 +1654,7 @@ proc makeAddr(n: PNode): PNode =
|
||||
result.typ = makePtrType(n.typ)
|
||||
|
||||
proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc) =
|
||||
if p.config.selectedGC == gcDestructors:
|
||||
if optSeqDestructors in p.config.globalOptions:
|
||||
e.sons[1] = makeAddr(e[1])
|
||||
genCall(p, e, d)
|
||||
return
|
||||
@@ -1683,7 +1683,7 @@ proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc) =
|
||||
gcUsage(p.config, e)
|
||||
|
||||
proc genSetLengthStr(p: BProc, e: PNode, d: var TLoc) =
|
||||
if p.config.selectedGC == gcDestructors:
|
||||
if optSeqDestructors in p.config.globalOptions:
|
||||
binaryStmtAddr(p, e, d, "setLengthStrV2")
|
||||
else:
|
||||
var a, b, call: TLoc
|
||||
@@ -1897,7 +1897,7 @@ proc genSomeCast(p: BProc, e: PNode, d: var TLoc) =
|
||||
if srcTyp.kind in {tyPtr, tyPointer} and etyp.kind in IntegralTypes:
|
||||
putIntoDest(p, d, e, "(($1) (ptrdiff_t) ($2))" %
|
||||
[getTypeDesc(p.module, e.typ), rdCharLoc(a)], a.storage)
|
||||
elif p.config.selectedGC == gcDestructors and etyp.kind in {tySequence, tyString}:
|
||||
elif optSeqDestructors in p.config.globalOptions and etyp.kind in {tySequence, tyString}:
|
||||
putIntoDest(p, d, e, "(*($1*) (&$2))" %
|
||||
[getTypeDesc(p.module, e.typ), rdCharLoc(a)], a.storage)
|
||||
else:
|
||||
@@ -2025,7 +2025,7 @@ proc genMove(p: BProc; n: PNode; d: var TLoc) =
|
||||
resetLoc(p, a)
|
||||
|
||||
proc genDestroy(p: BProc; n: PNode) =
|
||||
if p.config.selectedGC == gcDestructors:
|
||||
if optSeqDestructors in p.config.globalOptions:
|
||||
let arg = n[1].skipAddr
|
||||
let t = arg.typ.skipTypes(abstractInst)
|
||||
case t.kind
|
||||
@@ -2120,7 +2120,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||
|
||||
of mConStrStr: genStrConcat(p, e, d)
|
||||
of mAppendStrCh:
|
||||
if p.config.selectedGC == gcDestructors:
|
||||
if optSeqDestructors in p.config.globalOptions:
|
||||
binaryStmtAddr(p, e, d, "nimAddCharV1")
|
||||
else:
|
||||
var dest, b, call: TLoc
|
||||
@@ -2131,7 +2131,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||
genAssignment(p, dest, call, {})
|
||||
of mAppendStrStr: genStrAppend(p, e, d)
|
||||
of mAppendSeqElem:
|
||||
if p.config.selectedGC == gcDestructors:
|
||||
if optSeqDestructors in p.config.globalOptions:
|
||||
e.sons[1] = makeAddr(e[1])
|
||||
genCall(p, e, d)
|
||||
else:
|
||||
@@ -2148,7 +2148,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||
of mCStrToStr: genDollar(p, e, d, "#cstrToNimstr($1)")
|
||||
of mStrToStr, mUnown: expr(p, e.sons[1], d)
|
||||
of mEnumToStr:
|
||||
if optNimV2 in p.config.globalOptions:
|
||||
if optTinyRtti in p.config.globalOptions:
|
||||
genEnumToStr(p, e, d)
|
||||
else:
|
||||
genRepr(p, e, d)
|
||||
@@ -2423,7 +2423,7 @@ proc upConv(p: BProc, n: PNode, d: var TLoc) =
|
||||
while t.kind == tyObject and t.sons[0] != nil:
|
||||
add(r, ".Sup")
|
||||
t = skipTypes(t.sons[0], skipPtrs)
|
||||
let checkFor = if optNimV2 in p.config.globalOptions:
|
||||
let checkFor = if optTinyRtti in p.config.globalOptions:
|
||||
genTypeInfo2Name(p.module, dest)
|
||||
else:
|
||||
genTypeInfo(p.module, dest, n.info)
|
||||
@@ -2713,7 +2713,7 @@ proc getDefaultValue(p: BProc; typ: PType; info: TLineInfo): Rope =
|
||||
tyTyped, tyTypeDesc, tyStatic, tyRef, tyNil:
|
||||
result = rope"NIM_NIL"
|
||||
of tyString, tySequence:
|
||||
if p.config.selectedGC == gcDestructors:
|
||||
if optSeqDestructors in p.config.globalOptions:
|
||||
result = rope"{0, NIM_NIL}"
|
||||
else:
|
||||
result = rope"NIM_NIL"
|
||||
@@ -2855,7 +2855,7 @@ proc genConstExpr(p: BProc, n: PNode): Rope =
|
||||
of nkBracket, nkPar, nkTupleConstr, nkClosure:
|
||||
var t = skipTypes(n.typ, abstractInstOwned)
|
||||
if t.kind == tySequence:
|
||||
if p.config.selectedGC == gcDestructors:
|
||||
if optSeqDestructors in p.config.globalOptions:
|
||||
result = genConstSeqV2(p, n, n.typ)
|
||||
else:
|
||||
result = genConstSeq(p, n, n.typ)
|
||||
@@ -2879,7 +2879,7 @@ proc genConstExpr(p: BProc, n: PNode): Rope =
|
||||
of nkObjConstr:
|
||||
result = genConstObjConstr(p, n)
|
||||
of nkStrLit..nkTripleStrLit:
|
||||
if p.config.selectedGC == gcDestructors:
|
||||
if optSeqDestructors in p.config.globalOptions:
|
||||
result = genStringLiteralV2Const(p.module, n)
|
||||
else:
|
||||
var d: TLoc
|
||||
|
||||
@@ -17,7 +17,7 @@ const
|
||||
|
||||
proc getTraverseProc(p: BProc, v: PSym): Rope =
|
||||
if p.config.selectedGC in {gcMarkAndSweep, gcDestructors, gcV2, gcRefc} and
|
||||
optNimV2 notin p.config.globalOptions and
|
||||
optOwnedRefs notin p.config.globalOptions and
|
||||
containsGarbageCollectedRef(v.loc.t):
|
||||
# we register a specialized marked proc here; this has the advantage
|
||||
# that it works out of the box for thread local storage then :-)
|
||||
@@ -692,7 +692,7 @@ proc genRaiseStmt(p: BProc, t: PNode) =
|
||||
[e, makeCString(typ.sym.name.s),
|
||||
makeCString(if p.prc != nil: p.prc.name.s else: p.module.module.name.s),
|
||||
quotedFilename(p.config, t.info), toLinenumber(t.info)])
|
||||
if optNimV2 in p.config.globalOptions:
|
||||
if optOwnedRefs in p.config.globalOptions:
|
||||
lineCg(p, cpsStmts, "$1 = NIM_NIL;$n", [e])
|
||||
else:
|
||||
genLineDir(p, t)
|
||||
@@ -1057,7 +1057,7 @@ proc genTry(p: BProc, t: PNode, d: var TLoc) =
|
||||
for j in 0 .. blen - 2:
|
||||
assert(t.sons[i].sons[j].kind == nkType)
|
||||
if orExpr != nil: add(orExpr, "||")
|
||||
let checkFor = if optNimV2 in p.config.globalOptions:
|
||||
let checkFor = if optTinyRtti in p.config.globalOptions:
|
||||
genTypeInfo2Name(p.module, t[i][j].typ)
|
||||
else:
|
||||
genTypeInfo(p.module, t[i][j].typ, t[i][j].info)
|
||||
@@ -1220,7 +1220,7 @@ proc asgnFieldDiscriminant(p: BProc, e: PNode) =
|
||||
getTemp(p, a.t, tmp)
|
||||
expr(p, e.sons[1], tmp)
|
||||
let field = dotExpr.sons[1].sym
|
||||
if optNimV2 in p.config.globalOptions:
|
||||
if optTinyRtti in p.config.globalOptions:
|
||||
let t = dotExpr[0].typ.skipTypes(abstractInst)
|
||||
var oldVal, newVal: TLoc
|
||||
genCaseObjDiscMapping(p, e[0], t, field, oldVal)
|
||||
|
||||
@@ -17,7 +17,7 @@ type
|
||||
visitorFrmt: string
|
||||
|
||||
const
|
||||
visitorFrmt = "#nimGCvisit((void*)$1, $2);$n"
|
||||
visitorFrmt = "#nimGCvisit((void*)$1, $2);$n"
|
||||
|
||||
proc genTraverseProc(c: TTraversalClosure, accessor: Rope, typ: PType)
|
||||
proc genCaseRange(p: BProc, branch: PNode)
|
||||
@@ -104,7 +104,8 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, typ: PType) =
|
||||
elif containsGarbageCollectedRef(typ.lastSon):
|
||||
# destructor based seqs are themselves not traced but their data is, if
|
||||
# they contain a GC'ed type:
|
||||
genTraverseProcSeq(c, accessor, typ)
|
||||
lineCg(p, cpsStmts, "#nimGCvisitSeq((void*)$1, $2);$n", [accessor, c.visitorFrmt])
|
||||
#genTraverseProcSeq(c, accessor, typ)
|
||||
of tyString:
|
||||
if tfHasAsgn notin typ.flags:
|
||||
lineCg(p, cpsStmts, visitorFrmt, [accessor, c.visitorFrmt])
|
||||
|
||||
@@ -356,7 +356,7 @@ proc addForwardStructFormat(m: BModule, structOrUnion: Rope, typename: Rope) =
|
||||
m.s[cfsForwardTypes].addf "typedef $1 $2 $2;$n", [structOrUnion, typename]
|
||||
|
||||
proc seqStar(m: BModule): string =
|
||||
if m.config.selectedGC == gcDestructors: result = ""
|
||||
if optSeqDestructors in m.config.globalOptions: result = ""
|
||||
else: result = "*"
|
||||
|
||||
proc getTypeForward(m: BModule, typ: PType; sig: SigHash): Rope =
|
||||
@@ -390,7 +390,7 @@ proc getTypeDescWeak(m: BModule; t: PType; check: var IntSet): Rope =
|
||||
pushType(m, t)
|
||||
of tySequence:
|
||||
let sig = hashType(t)
|
||||
if m.config.selectedGC == gcDestructors:
|
||||
if optSeqDestructors in m.config.globalOptions:
|
||||
if skipTypes(etB.sons[0], typedescInst).kind == tyEmpty:
|
||||
internalError(m.config, "cannot map the empty seq type to a C type")
|
||||
|
||||
@@ -710,7 +710,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
|
||||
result = name & star
|
||||
m.typeCache[sig] = result
|
||||
of tySequence:
|
||||
if m.config.selectedGC == gcDestructors:
|
||||
if optSeqDestructors in m.config.globalOptions:
|
||||
result = getTypeDescWeak(m, et, check) & star
|
||||
m.typeCache[sig] = result
|
||||
else:
|
||||
@@ -770,7 +770,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
|
||||
"void* ClE_0;$n} $1;$n",
|
||||
[result, rettype, desc])
|
||||
of tySequence:
|
||||
if m.config.selectedGC == gcDestructors:
|
||||
if optSeqDestructors in m.config.globalOptions:
|
||||
result = getTypeDescWeak(m, t, check)
|
||||
else:
|
||||
# we cannot use getTypeForward here because then t would be associated
|
||||
@@ -926,7 +926,7 @@ proc finishTypeDescriptions(m: BModule) =
|
||||
var check = initIntSet()
|
||||
while i < len(m.typeStack):
|
||||
let t = m.typeStack[i]
|
||||
if m.config.selectedGC == gcDestructors and t.skipTypes(abstractInst).kind == tySequence:
|
||||
if optSeqDestructors in m.config.globalOptions and t.skipTypes(abstractInst).kind == tySequence:
|
||||
seqV2ContentType(m, t, check)
|
||||
else:
|
||||
discard getTypeDescAux(m, t, check)
|
||||
@@ -1351,7 +1351,7 @@ proc genTypeInfo(m: BModule, t: PType; info: TLineInfo): Rope =
|
||||
genTupleInfo(m, x, x, result, info)
|
||||
of tySequence:
|
||||
genTypeInfoAux(m, t, t, result, info)
|
||||
if m.config.selectedGC != gcDestructors:
|
||||
if optSeqDestructors notin m.config.globalOptions:
|
||||
if m.config.selectedGC >= gcMarkAndSweep:
|
||||
let markerProc = genTraverseProc(m, origType, sig)
|
||||
addf(m.s[cfsTypeInit3], "$1.marker = $2;$n", [tiNameForHcr(m, result), markerProc])
|
||||
@@ -1365,7 +1365,7 @@ proc genTypeInfo(m: BModule, t: PType; info: TLineInfo): Rope =
|
||||
of tySet: genSetInfo(m, t, result, info)
|
||||
of tyEnum: genEnumInfo(m, t, result, info)
|
||||
of tyObject:
|
||||
if optNimV2 in m.config.globalOptions:
|
||||
if optTinyRtti in m.config.globalOptions:
|
||||
genObjectInfoV2(m, t, origType, result, info)
|
||||
else:
|
||||
genObjectInfo(m, t, origType, result, info)
|
||||
|
||||
@@ -293,13 +293,13 @@ proc lenField(p: BProc): Rope =
|
||||
result = rope(if p.module.compileToCpp: "len" else: "Sup.len")
|
||||
|
||||
proc lenExpr(p: BProc; a: TLoc): Rope =
|
||||
if p.config.selectedGC == gcDestructors:
|
||||
if optSeqDestructors in p.config.globalOptions:
|
||||
result = rdLoc(a) & ".len"
|
||||
else:
|
||||
result = "($1 ? $1->$2 : 0)" % [rdLoc(a), lenField(p)]
|
||||
|
||||
proc dataField(p: BProc): Rope =
|
||||
if p.config.selectedGC == gcDestructors:
|
||||
if optSeqDestructors in p.config.globalOptions:
|
||||
result = rope".p->data"
|
||||
else:
|
||||
result = rope"->data"
|
||||
@@ -347,7 +347,7 @@ proc genObjectInit(p: BProc, section: TCProcSection, t: PType, a: TLoc,
|
||||
s = skipTypes(s.sons[0], skipPtrs)
|
||||
linefmt(p, section, "$1.m_type = $2;$n", [r, genTypeInfo(p.module, t, a.lode.info)])
|
||||
of frEmbedded:
|
||||
if optNimV2 in p.config.globalOptions:
|
||||
if optTinyRtti in p.config.globalOptions:
|
||||
localError(p.config, p.prc.info,
|
||||
"complex object initialization is not supported with --newruntime")
|
||||
# worst case for performance:
|
||||
@@ -377,10 +377,10 @@ proc isComplexValueType(t: PType): bool {.inline.} =
|
||||
(t.kind == tyProc and t.callConv == ccClosure)
|
||||
|
||||
proc resetLoc(p: BProc, loc: var TLoc) =
|
||||
let containsGcRef = p.config.selectedGC != gcDestructors and containsGarbageCollectedRef(loc.t)
|
||||
let containsGcRef = optSeqDestructors notin p.config.globalOptions and containsGarbageCollectedRef(loc.t)
|
||||
let typ = skipTypes(loc.t, abstractVarRange)
|
||||
if isImportedCppType(typ): return
|
||||
if p.config.selectedGC == gcDestructors and typ.kind in {tyString, tySequence}:
|
||||
if optSeqDestructors in p.config.globalOptions and typ.kind in {tyString, tySequence}:
|
||||
assert rdLoc(loc) != nil
|
||||
linefmt(p, cpsStmts, "$1.len = 0; $1.p = NIM_NIL;$n", [rdLoc(loc)])
|
||||
elif not isComplexValueType(typ):
|
||||
@@ -411,7 +411,7 @@ proc resetLoc(p: BProc, loc: var TLoc) =
|
||||
|
||||
proc constructLoc(p: BProc, loc: TLoc, isTemp = false) =
|
||||
let typ = loc.t
|
||||
if p.config.selectedGC == gcDestructors and skipTypes(typ, abstractInst).kind in {tyString, tySequence}:
|
||||
if optSeqDestructors in p.config.globalOptions and skipTypes(typ, abstractInst).kind in {tyString, tySequence}:
|
||||
linefmt(p, cpsStmts, "$1.len = 0; $1.p = NIM_NIL;$n", [rdLoc(loc)])
|
||||
elif not isComplexValueType(typ):
|
||||
linefmt(p, cpsStmts, "$1 = ($2)0;$n", [rdLoc(loc),
|
||||
@@ -997,13 +997,14 @@ proc genProcAux(m: BModule, prc: PSym) =
|
||||
closureSetup(p, prc)
|
||||
genStmts(p, procBody) # modifies p.locals, p.init, etc.
|
||||
var generatedProc: Rope
|
||||
generatedProc.genCLineDir prc.info, m.config
|
||||
if sfNoReturn in prc.flags:
|
||||
if hasDeclspec in extccomp.CC[p.config.cCompiler].props:
|
||||
header = "__declspec(noreturn) " & header
|
||||
if sfPure in prc.flags:
|
||||
if hasDeclspec in extccomp.CC[p.config.cCompiler].props:
|
||||
header = "__declspec(naked) " & header
|
||||
generatedProc = ropecg(p.module, "$N$1 {$n$2$3$4}$N$N",
|
||||
generatedProc.add ropecg(p.module, "$1 {$n$2$3$4}$N$N",
|
||||
[header, p.s(cpsLocals), p.s(cpsInit), p.s(cpsStmts)])
|
||||
else:
|
||||
if m.hcrOn and isReloadable(m, prc):
|
||||
@@ -1011,7 +1012,7 @@ proc genProcAux(m: BModule, prc: PSym) =
|
||||
# This fixes the use of methods and also the case when 2 functions within the same module
|
||||
# call each other using directly the "_actual" versions (an optimization) - see issue #11608
|
||||
addf(m.s[cfsProcHeaders], "$1;\n", [header])
|
||||
generatedProc = ropecg(p.module, "$N$1 {$N", [header])
|
||||
generatedProc.add ropecg(p.module, "$1 {", [header])
|
||||
add(generatedProc, initGCFrame(p))
|
||||
if optStackTrace in prc.options:
|
||||
add(generatedProc, p.s(cpsLocals))
|
||||
|
||||
@@ -683,7 +683,8 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
|
||||
n[0] = ex
|
||||
result.add(n)
|
||||
|
||||
of nkCast, nkHiddenStdConv, nkHiddenSubConv, nkConv, nkObjDownConv:
|
||||
of nkCast, nkHiddenStdConv, nkHiddenSubConv, nkConv, nkObjDownConv,
|
||||
nkDerefExpr, nkHiddenDeref:
|
||||
var ns = false
|
||||
for i in 0 ..< n.len:
|
||||
n[i] = ctx.lowerStmtListExprs(n[i], ns)
|
||||
@@ -757,7 +758,7 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
|
||||
n[0] = newSymNode(ctx.g.getSysSym(n[0].info, "true"))
|
||||
n[1] = newBody
|
||||
|
||||
of nkDotExpr:
|
||||
of nkDotExpr, nkCheckedFieldExpr:
|
||||
var ns = false
|
||||
n[0] = ctx.lowerStmtListExprs(n[0], ns)
|
||||
if ns:
|
||||
|
||||
@@ -452,6 +452,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
of "destructors":
|
||||
conf.selectedGC = gcDestructors
|
||||
defineSymbol(conf.symbols, "gcdestructors")
|
||||
incl conf.globalOptions, optSeqDestructors
|
||||
of "go":
|
||||
conf.selectedGC = gcGo
|
||||
defineSymbol(conf.symbols, "gogc")
|
||||
@@ -758,10 +759,17 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
if pass in {passCmd2, passPP}:
|
||||
doAssert(conf != nil)
|
||||
incl(conf.features, destructor)
|
||||
incl(conf.globalOptions, optNimV2)
|
||||
incl(conf.globalOptions, optTinyRtti)
|
||||
incl(conf.globalOptions, optOwnedRefs)
|
||||
incl(conf.globalOptions, optSeqDestructors)
|
||||
defineSymbol(conf.symbols, "nimV2")
|
||||
conf.selectedGC = gcDestructors
|
||||
defineSymbol(conf.symbols, "gcdestructors")
|
||||
defineSymbol(conf.symbols, "nimSeqsV2")
|
||||
of "seqsv2":
|
||||
processOnOffSwitchG(conf, {optSeqDestructors}, arg, pass, info)
|
||||
if pass in {passCmd2, passPP}:
|
||||
defineSymbol(conf.symbols, "nimSeqsV2")
|
||||
of "stylecheck":
|
||||
case arg.normalize
|
||||
of "off": conf.globalOptions = conf.globalOptions - {optStyleHint, optStyleError}
|
||||
|
||||
@@ -53,7 +53,6 @@ proc initDefines*(symbols: StringTableRef) =
|
||||
defineSymbol("nimKnowsNimvm")
|
||||
defineSymbol("nimArrIdx")
|
||||
defineSymbol("nimHasalignOf")
|
||||
defineSymbol("nimNewShiftOps")
|
||||
defineSymbol("nimDistros")
|
||||
defineSymbol("nimHasCppDefine")
|
||||
defineSymbol("nimGenericInOutFlags")
|
||||
@@ -99,3 +98,4 @@ proc initDefines*(symbols: StringTableRef) =
|
||||
defineSymbol("nimHasStyleChecks")
|
||||
defineSymbol("nimToOpenArrayCString")
|
||||
defineSymbol("nimHasUsed")
|
||||
defineSymbol("nimFixedForwardGeneric")
|
||||
|
||||
@@ -63,8 +63,8 @@ compiler gcc:
|
||||
result = (
|
||||
name: "gcc",
|
||||
objExt: "o",
|
||||
optSpeed: " -O3 ",
|
||||
optSize: " -Os ",
|
||||
optSpeed: " -O3 -fno-ident",
|
||||
optSize: " -Os -fno-ident",
|
||||
compilerExe: "gcc",
|
||||
cppCompiler: "g++",
|
||||
compileTmpl: "-c $options $include -o $objfile $file",
|
||||
@@ -662,6 +662,10 @@ proc addExternalFileToCompile*(conf: ConfigRef; c: var Cfile) =
|
||||
if optForceFullMake notin conf.globalOptions and fileExists(c.obj) and
|
||||
not externalFileChanged(conf, c):
|
||||
c.flags.incl CfileFlag.Cached
|
||||
else:
|
||||
# make sure Nim keeps recompiling the external file on reruns
|
||||
# if compilation is not successful
|
||||
discard tryRemoveFile(c.obj.string)
|
||||
conf.toCompile.add(c)
|
||||
|
||||
proc addExternalFileToCompile*(conf: ConfigRef; filename: AbsoluteFile) =
|
||||
|
||||
@@ -436,7 +436,7 @@ proc sameTree*(a, b: PNode): bool =
|
||||
if not result and a.sym.magic != mNone:
|
||||
result = a.sym.magic == b.sym.magic or sameOpr(a.sym, b.sym)
|
||||
of nkIdent: result = a.ident.id == b.ident.id
|
||||
of nkCharLit..nkInt64Lit: result = a.intVal == b.intVal
|
||||
of nkCharLit..nkUInt64Lit: result = a.intVal == b.intVal
|
||||
of nkFloatLit..nkFloat64Lit: result = a.floatVal == b.floatVal
|
||||
of nkStrLit..nkTripleStrLit: result = a.strVal == b.strVal
|
||||
of nkType: result = a.typ == b.typ
|
||||
|
||||
@@ -486,7 +486,7 @@ proc p(n: PNode; c: var Con): PNode =
|
||||
result.add n[0]
|
||||
result.add p(n[1], c)
|
||||
of nkRaiseStmt:
|
||||
if optNimV2 in c.graph.config.globalOptions and n[0].kind != nkEmpty:
|
||||
if optOwnedRefs in c.graph.config.globalOptions and n[0].kind != nkEmpty:
|
||||
if n[0].kind in nkCallKinds:
|
||||
let call = p(n[0], c)
|
||||
result = copyNode(n)
|
||||
@@ -636,7 +636,7 @@ proc reverseDestroys(destroys: seq[PNode]): seq[PNode] =
|
||||
result.add destroys[i]
|
||||
|
||||
proc injectDestructorCalls*(g: ModuleGraph; owner: PSym; n: PNode): PNode =
|
||||
if sfGeneratedOp in owner.flags or isInlineIterator(owner): return n
|
||||
if sfGeneratedOp in owner.flags or (owner.kind == skIterator and isInlineIterator(owner.typ)): return n
|
||||
var c: Con
|
||||
c.owner = owner
|
||||
c.destroys = newNodeI(nkStmtList, n.info)
|
||||
|
||||
@@ -436,7 +436,7 @@ proc divMod*(dividend, divisor: Int128): tuple[quotient, remainder: Int128] =
|
||||
result.quotient = -quotient
|
||||
else:
|
||||
result.quotient = quotient
|
||||
if isNegativeB:
|
||||
if isNegativeA:
|
||||
result.remainder = -dividend
|
||||
else:
|
||||
result.remainder = dividend
|
||||
@@ -676,3 +676,12 @@ when isMainModule:
|
||||
|
||||
doAssert $high(Int128) == "170141183460469231731687303715884105727"
|
||||
doAssert $low(Int128) == "-170141183460469231731687303715884105728"
|
||||
|
||||
var ma = 100'i64
|
||||
var mb = 13
|
||||
|
||||
# sign correctness
|
||||
doAssert divMod(toInt128( ma),toInt128( mb)) == (toInt128( ma div mb), toInt128( ma mod mb))
|
||||
doAssert divMod(toInt128(-ma),toInt128( mb)) == (toInt128(-ma div mb), toInt128(-ma mod mb))
|
||||
doAssert divMod(toInt128( ma),toInt128(-mb)) == (toInt128( ma div -mb), toInt128( ma mod -mb))
|
||||
doAssert divMod(toInt128(-ma),toInt128(-mb)) == (toInt128(-ma div -mb), toInt128(-ma mod -mb))
|
||||
|
||||
@@ -257,7 +257,7 @@ proc liftIterSym*(g: ModuleGraph; n: PNode; owner: PSym): PNode =
|
||||
result.add(v)
|
||||
# add 'new' statement:
|
||||
result.add newCall(getSysSym(g, n.info, "internalNew"), env)
|
||||
if optNimV2 in g.config.globalOptions:
|
||||
if optOwnedRefs in g.config.globalOptions:
|
||||
createTypeBoundOps(g, nil, env.typ, n.info)
|
||||
result.add makeClosure(g, iter, env, n.info)
|
||||
|
||||
@@ -323,7 +323,7 @@ proc getEnvTypeForOwner(c: var DetectionPass; owner: PSym;
|
||||
c.ownerToType[owner.id] = result
|
||||
|
||||
proc asOwnedRef(c: DetectionPass; t: PType): PType =
|
||||
if optNimV2 in c.graph.config.globalOptions:
|
||||
if optOwnedRefs in c.graph.config.globalOptions:
|
||||
assert t.kind == tyRef
|
||||
result = newType(tyOwned, t.owner)
|
||||
result.flags.incl tfHasOwned
|
||||
@@ -342,9 +342,9 @@ proc createUpField(c: var DetectionPass; dest, dep: PSym; info: TLineInfo) =
|
||||
let obj = refObj.skipTypes({tyOwned, tyRef})
|
||||
# The assumption here is that gcDestructors means we cannot deal
|
||||
# with cycles properly, so it's better to produce a weak ref (=ptr) here.
|
||||
# This seems to be generally correct but since it's a bit risky it's only
|
||||
# enabled for gcDestructors.
|
||||
let fieldType = if false: # c.graph.config.selectedGC == gcDestructors:
|
||||
# This seems to be generally correct but since it's a bit risky it's disabled
|
||||
# for now.
|
||||
let fieldType = if false: # optSeqDestructors in c.graph.config.globalOptions:
|
||||
c.getEnvTypeForOwnerUp(dep, info) #getHiddenParam(dep).typ
|
||||
else:
|
||||
c.getEnvTypeForOwner(dep, info)
|
||||
@@ -477,7 +477,7 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
|
||||
w = up
|
||||
of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit,
|
||||
nkTemplateDef, nkTypeSection, nkProcDef, nkMethodDef,
|
||||
nkConverterDef, nkMacroDef, nkFuncDef, nkCommentStmt:
|
||||
nkConverterDef, nkMacroDef, nkFuncDef, nkCommentStmt, nkTypeOfExpr:
|
||||
discard
|
||||
of nkLambdaKinds, nkIteratorDef:
|
||||
if n.typ != nil:
|
||||
@@ -542,7 +542,7 @@ proc setupEnvVar(owner: PSym; d: DetectionPass;
|
||||
localError d.graph.config, owner.info, "internal error: could not determine closure type"
|
||||
result = newEnvVar(d.graph.cache, owner, asOwnedRef(d, envVarType), info)
|
||||
c.envVars[owner.id] = result
|
||||
if optNimV2 in d.graph.config.globalOptions:
|
||||
if optOwnedRefs in d.graph.config.globalOptions:
|
||||
var v = newSym(skVar, getIdent(d.graph.cache, envName & "Alt"), owner, info)
|
||||
v.flags = {sfShadowed, sfGeneratedOp}
|
||||
v.typ = envVarType
|
||||
@@ -572,14 +572,14 @@ proc rawClosureCreation(owner: PSym;
|
||||
var v = newNodeI(nkVarSection, env.info)
|
||||
addVar(v, env)
|
||||
result.add(v)
|
||||
if optNimV2 in d.graph.config.globalOptions:
|
||||
if optOwnedRefs in d.graph.config.globalOptions:
|
||||
let unowned = c.unownedEnvVars[owner.id]
|
||||
assert unowned != nil
|
||||
addVar(v, unowned)
|
||||
|
||||
# add 'new' statement:
|
||||
result.add(newCall(getSysSym(d.graph, env.info, "internalNew"), env))
|
||||
if optNimV2 in d.graph.config.globalOptions:
|
||||
if optOwnedRefs in d.graph.config.globalOptions:
|
||||
let unowned = c.unownedEnvVars[owner.id]
|
||||
assert unowned != nil
|
||||
let env2 = copyTree(env)
|
||||
@@ -608,12 +608,12 @@ proc rawClosureCreation(owner: PSym;
|
||||
localError(d.graph.config, env.info, "internal error: cannot create up reference")
|
||||
# we are not in the sem'check phase anymore! so pass 'nil' for the PContext
|
||||
# and hope for the best:
|
||||
if optNimV2 in d.graph.config.globalOptions:
|
||||
if optOwnedRefs in d.graph.config.globalOptions:
|
||||
createTypeBoundOps(d.graph, nil, env.typ, owner.info)
|
||||
|
||||
proc finishClosureCreation(owner: PSym; d: DetectionPass; c: LiftingPass;
|
||||
info: TLineInfo; res: PNode) =
|
||||
if optNimV2 in d.graph.config.globalOptions:
|
||||
if optOwnedRefs in d.graph.config.globalOptions:
|
||||
let unowned = c.unownedEnvVars[owner.id]
|
||||
assert unowned != nil
|
||||
let nilLit = newNodeIT(nkNilLit, info, unowned.typ)
|
||||
@@ -637,7 +637,7 @@ proc closureCreationForIter(iter: PNode;
|
||||
addVar(vs, vnode)
|
||||
result.add(vs)
|
||||
result.add(newCall(getSysSym(d.graph, iter.info, "internalNew"), vnode))
|
||||
if optNimV2 in d.graph.config.globalOptions:
|
||||
if optOwnedRefs in d.graph.config.globalOptions:
|
||||
createTypeBoundOps(d.graph, nil, vnode.typ, iter.info)
|
||||
|
||||
let upField = lookupInRecord(v.typ.skipTypes({tyOwned, tyRef}).n, getIdent(d.graph.cache, upName))
|
||||
@@ -653,7 +653,7 @@ proc closureCreationForIter(iter: PNode;
|
||||
proc accessViaEnvVar(n: PNode; owner: PSym; d: DetectionPass;
|
||||
c: var LiftingPass): PNode =
|
||||
var access = setupEnvVar(owner, d, c, n.info)
|
||||
if optNimV2 in d.graph.config.globalOptions:
|
||||
if optOwnedRefs in d.graph.config.globalOptions:
|
||||
access = c.unownedEnvVars[owner.id]
|
||||
let obj = access.typ.skipTypes({tyOwned, tyRef})
|
||||
let field = getFieldFromObj(obj, n.sym)
|
||||
@@ -760,6 +760,8 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass;
|
||||
n[0].sons[1] = liftCapturedVars(n[0].sons[1], owner, d, c)
|
||||
else:
|
||||
n.sons[0] = liftCapturedVars(n[0], owner, d, c)
|
||||
of nkTypeOfExpr:
|
||||
result = n
|
||||
else:
|
||||
if owner.isIterator:
|
||||
if nfLL in n.flags:
|
||||
@@ -921,7 +923,7 @@ proc liftForLoop*(g: ModuleGraph; body: PNode; owner: PSym): PNode =
|
||||
result.add(v)
|
||||
# add 'new' statement:
|
||||
result.add(newCall(getSysSym(g, env.info, "internalNew"), env.newSymNode))
|
||||
if optNimV2 in g.config.globalOptions:
|
||||
if optOwnedRefs in g.config.globalOptions:
|
||||
createTypeBoundOps(g, nil, env.typ, body.info)
|
||||
|
||||
elif op.kind == nkStmtListExpr:
|
||||
|
||||
@@ -160,13 +160,11 @@ proc guidingInd(em: Emitter; pos: int): int =
|
||||
inc i
|
||||
result = -1
|
||||
|
||||
proc closeEmitter*(em: var Emitter) =
|
||||
proc renderTokens*(em: var Emitter): string =
|
||||
## Render Emitter tokens to a string of code
|
||||
template defaultCase() =
|
||||
content.add em.tokens[i]
|
||||
inc lineLen, em.tokens[i].len
|
||||
|
||||
let outFile = em.config.absOutFile
|
||||
|
||||
var content = newStringOfCap(16_000)
|
||||
var maxLhs = 0
|
||||
var lineLen = 0
|
||||
@@ -243,6 +241,11 @@ proc closeEmitter*(em: var Emitter) =
|
||||
defaultCase()
|
||||
inc i
|
||||
|
||||
return content
|
||||
|
||||
proc writeOut*(em: Emitter, content: string) =
|
||||
## Write to disk
|
||||
let outFile = em.config.absOutFile
|
||||
if fileExists(outFile) and readFile(outFile.string) == content:
|
||||
discard "do nothing, see #9499"
|
||||
return
|
||||
@@ -253,6 +256,11 @@ proc closeEmitter*(em: var Emitter) =
|
||||
f.llStreamWrite content
|
||||
llStreamClose(f)
|
||||
|
||||
proc closeEmitter*(em: var Emitter) =
|
||||
## Renders emitter tokens and write to a file
|
||||
let content = renderTokens(em)
|
||||
em.writeOut(content)
|
||||
|
||||
proc wr(em: var Emitter; x: string; lt: LayoutToken) =
|
||||
em.tokens.add x
|
||||
em.kinds.add lt
|
||||
|
||||
@@ -129,7 +129,7 @@ proc newDeepCopyCall(op: PSym; x, y: PNode): PNode =
|
||||
result = newAsgnStmt(x, newOpCall(op, y))
|
||||
|
||||
proc useNoGc(c: TLiftCtx; t: PType): bool {.inline.} =
|
||||
result = c.g.config.selectedGC == gcDestructors and
|
||||
result = optSeqDestructors in c.g.config.globalOptions and
|
||||
({tfHasGCedMem, tfHasOwned} * t.flags != {} or t.isGCedMem)
|
||||
|
||||
proc instantiateGeneric(c: var TLiftCtx; op: PSym; t, typeInst: PType): PSym =
|
||||
@@ -142,7 +142,7 @@ proc instantiateGeneric(c: var TLiftCtx; op: PSym; t, typeInst: PType): PSym =
|
||||
|
||||
proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode;
|
||||
field: var PSym): bool =
|
||||
if c.g.config.selectedGC == gcDestructors:
|
||||
if optSeqDestructors in c.g.config.globalOptions:
|
||||
let op = field
|
||||
if field != nil and sfOverriden in field.flags:
|
||||
if sfError in op.flags:
|
||||
@@ -411,7 +411,7 @@ proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
call.sons[0] = newSymNode(createMagic(c.g, "deepCopy", mDeepCopy))
|
||||
call.sons[1] = y
|
||||
body.add newAsgnStmt(x, call)
|
||||
elif optNimV2 in c.g.config.globalOptions and
|
||||
elif optOwnedRefs in c.g.config.globalOptions and
|
||||
optRefCheck in c.g.config.options:
|
||||
let xx = genBuiltin(c.g, mAccessEnv, "accessEnv", x)
|
||||
xx.typ = getSysType(c.g, c.info, tyPointer)
|
||||
@@ -457,7 +457,7 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
tyPtr, tyOpt, tyUncheckedArray:
|
||||
defaultOp(c, t, body, x, y)
|
||||
of tyRef:
|
||||
if optNimV2 in c.g.config.globalOptions and
|
||||
if optOwnedRefs in c.g.config.globalOptions and
|
||||
optRefCheck in c.g.config.options:
|
||||
weakrefOp(c, t, body, x, y)
|
||||
else:
|
||||
@@ -469,7 +469,7 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
defaultOp(c, t, body, x, y)
|
||||
of tyOwned:
|
||||
let base = t.skipTypes(abstractInstOwned)
|
||||
if optNimV2 in c.g.config.globalOptions:
|
||||
if optOwnedRefs in c.g.config.globalOptions:
|
||||
case base.kind
|
||||
of tyRef:
|
||||
ownedRefOp(c, base, body, x, y)
|
||||
@@ -488,7 +488,7 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
of tySequence:
|
||||
if useNoGc(c, t):
|
||||
useSeqOrStrOp(c, t, body, x, y)
|
||||
elif c.g.config.selectedGC == gcDestructors:
|
||||
elif optSeqDestructors in c.g.config.globalOptions:
|
||||
# note that tfHasAsgn is propagated so we need the check on
|
||||
# 'selectedGC' here to determine if we have the new runtime.
|
||||
discard considerUserDefinedOp(c, t, body, x, y)
|
||||
|
||||
@@ -81,7 +81,11 @@ type # please make sure we have under 32 options
|
||||
optNoNimblePath
|
||||
optHotCodeReloading
|
||||
optDynlibOverrideAll
|
||||
optNimV2
|
||||
optSeqDestructors # active if the implementation uses the new
|
||||
# string/seq implementation based on destructors
|
||||
optTinyRtti # active if we use the new "tiny RTTI"
|
||||
# implementation
|
||||
optOwnedRefs # active if the Nim compiler knows about 'owned'.
|
||||
optMultiMethods
|
||||
optNimV019
|
||||
optBenchmarkVM # Enables cpuTime() in the VM
|
||||
@@ -118,7 +122,7 @@ type
|
||||
|
||||
IdeCmd* = enum
|
||||
ideNone, ideSug, ideCon, ideDef, ideUse, ideDus, ideChk, ideMod,
|
||||
ideHighlight, ideOutline, ideKnown, ideMsg
|
||||
ideHighlight, ideOutline, ideKnown, ideMsg, ideProject
|
||||
|
||||
Feature* = enum ## experimental features; DO NOT RENAME THESE!
|
||||
implicitDeref,
|
||||
@@ -662,21 +666,40 @@ proc findModule*(conf: ConfigRef; modulename, currentModule: string): AbsoluteFi
|
||||
|
||||
proc findProjectNimFile*(conf: ConfigRef; pkg: string): string =
|
||||
const extensions = [".nims", ".cfg", ".nimcfg", ".nimble"]
|
||||
var candidates: seq[string] = @[]
|
||||
var dir = pkg
|
||||
var
|
||||
candidates: seq[string] = @[]
|
||||
dir = pkg
|
||||
prev = dir
|
||||
nimblepkg = ""
|
||||
let pkgname = pkg.lastPathPart()
|
||||
while true:
|
||||
for k, f in os.walkDir(dir, relative=true):
|
||||
for k, f in os.walkDir(dir, relative = true):
|
||||
if k == pcFile and f != "config.nims":
|
||||
let (_, name, ext) = splitFile(f)
|
||||
if ext in extensions:
|
||||
let x = changeFileExt(dir / name, ".nim")
|
||||
if fileExists(x):
|
||||
candidates.add x
|
||||
if ext == ".nimble":
|
||||
if nimblepkg.len == 0:
|
||||
nimblepkg = name
|
||||
# Since nimble packages can have their source in a subfolder,
|
||||
# check the last folder we were in for a possible match.
|
||||
if dir != prev:
|
||||
let x = prev / x.extractFilename()
|
||||
if fileExists(x):
|
||||
candidates.add x
|
||||
else:
|
||||
# If we found more than one nimble file, chances are that we
|
||||
# missed the real project file, or this is an invalid nimble
|
||||
# package. Either way, bailing is the better choice.
|
||||
return ""
|
||||
let pkgname = if nimblepkg.len > 0: nimblepkg else: pkgname
|
||||
for c in candidates:
|
||||
# nim-foo foo or foo nfoo
|
||||
if (pkg in c) or (c in pkg): return c
|
||||
if candidates.len >= 1:
|
||||
if pkgname in c.extractFilename(): return c
|
||||
if candidates.len > 0:
|
||||
return candidates[0]
|
||||
prev = dir
|
||||
dir = parentDir(dir)
|
||||
if dir == "": break
|
||||
return ""
|
||||
@@ -709,6 +732,7 @@ proc parseIdeCmd*(s: string): IdeCmd =
|
||||
of "outline": ideOutline
|
||||
of "known": ideKnown
|
||||
of "msg": ideMsg
|
||||
of "project": ideProject
|
||||
else: ideNone
|
||||
|
||||
proc `$`*(c: IdeCmd): string =
|
||||
@@ -725,6 +749,7 @@ proc `$`*(c: IdeCmd): string =
|
||||
of ideOutline: "outline"
|
||||
of ideKnown: "known"
|
||||
of ideMsg: "msg"
|
||||
of ideProject: "project"
|
||||
|
||||
proc floatInt64Align*(conf: ConfigRef): int16 =
|
||||
## Returns either 4 or 8 depending on reasons.
|
||||
|
||||
@@ -47,12 +47,9 @@ proc demanglePackageName*(path: string): string =
|
||||
|
||||
proc withPackageName*(conf: ConfigRef; path: AbsoluteFile): AbsoluteFile =
|
||||
let x = getPackageName(conf, path.string)
|
||||
if x.len == 0:
|
||||
result = path
|
||||
let (p, file, ext) = path.splitFile
|
||||
if x == "stdlib":
|
||||
# Hot code reloading now relies on 'stdlib_system' names etc.
|
||||
result = p / RelativeFile((x & '_' & file) & ext)
|
||||
else:
|
||||
let (p, file, ext) = path.splitFile
|
||||
if x == "stdlib":
|
||||
# Hot code reloading now relies on 'stdlib_system' names etc.
|
||||
result = p / RelativeFile((x & '_' & file) & ext)
|
||||
else:
|
||||
result = p / RelativeFile(fakePackageName(conf, path))
|
||||
result = p / RelativeFile(fakePackageName(conf, path))
|
||||
|
||||
@@ -720,7 +720,7 @@ proc pragmaGuard(c: PContext; it: PNode; kind: TSymKind): PSym =
|
||||
proc semCustomPragma(c: PContext, n: PNode): PNode =
|
||||
var callNode: PNode
|
||||
|
||||
if n.kind == nkIdent:
|
||||
if n.kind in {nkIdent, nkSym}:
|
||||
# pragma -> pragma()
|
||||
callNode = newTree(nkCall, n)
|
||||
elif n.kind == nkExprColonExpr:
|
||||
|
||||
@@ -314,6 +314,7 @@ proc litAux(g: TSrcGen; n: PNode, x: BiggestInt, size: int): string =
|
||||
while result != nil and result.kind in {tyGenericInst, tyRange, tyVar, tyLent, tyDistinct,
|
||||
tyOrdinal, tyAlias, tySink}:
|
||||
result = lastSon(result)
|
||||
|
||||
let typ = n.typ.skip
|
||||
if typ != nil and typ.kind in {tyBool, tyEnum}:
|
||||
if sfPure in typ.sym.flags:
|
||||
@@ -324,11 +325,13 @@ proc litAux(g: TSrcGen; n: PNode, x: BiggestInt, size: int): string =
|
||||
if e.sym.position == x:
|
||||
result &= e.sym.name.s
|
||||
return
|
||||
|
||||
let y = x and ((1 shl (size*8)) - 1)
|
||||
if nfBase2 in n.flags: result = "0b" & toBin(y, size * 8)
|
||||
elif nfBase8 in n.flags: result = "0o" & toOct(y, size * 3)
|
||||
elif nfBase16 in n.flags: result = "0x" & toHex(y, size * 2)
|
||||
|
||||
if nfBase2 in n.flags: result = "0b" & toBin(x, size * 8)
|
||||
elif nfBase8 in n.flags:
|
||||
var y = if size < sizeof(BiggestInt): x and ((1 shl (size*8)) - 1)
|
||||
else: x
|
||||
result = "0o" & toOct(y, size * 3)
|
||||
elif nfBase16 in n.flags: result = "0x" & toHex(x, size * 2)
|
||||
else: result = $x
|
||||
|
||||
proc ulitAux(g: TSrcGen; n: PNode, x: BiggestInt, size: int): string =
|
||||
@@ -474,7 +477,7 @@ proc lsub(g: TSrcGen; n: PNode): int =
|
||||
len("if_:_")
|
||||
of nkElifExpr: result = lsons(g, n) + len("_elif_:_")
|
||||
of nkElseExpr: result = lsub(g, n.sons[0]) + len("_else:_") # type descriptions
|
||||
of nkTypeOfExpr: result = (if n.len > 0: lsub(g, n.sons[0]) else: 0)+len("type()")
|
||||
of nkTypeOfExpr: result = (if n.len > 0: lsub(g, n.sons[0]) else: 0)+len("typeof()")
|
||||
of nkRefTy: result = (if n.len > 0: lsub(g, n.sons[0])+1 else: 0) + len("ref")
|
||||
of nkPtrTy: result = (if n.len > 0: lsub(g, n.sons[0])+1 else: 0) + len("ptr")
|
||||
of nkVarTy: result = (if n.len > 0: lsub(g, n.sons[0])+1 else: 0) + len("var")
|
||||
@@ -1195,7 +1198,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
|
||||
putWithSpace(g, tkColon, ":")
|
||||
gsub(g, n, 0)
|
||||
of nkTypeOfExpr:
|
||||
put(g, tkType, "type")
|
||||
put(g, tkType, "typeof")
|
||||
put(g, tkParLe, "(")
|
||||
if n.len > 0: gsub(g, n.sons[0])
|
||||
put(g, tkParRi, ")")
|
||||
|
||||
@@ -217,7 +217,7 @@ proc runNimScript*(cache: IdentCache; scriptName: AbsoluteFile;
|
||||
let oldGlobalOptions = conf.globalOptions
|
||||
let oldSelectedGC = conf.selectedGC
|
||||
undefSymbol(conf.symbols, "nimv2")
|
||||
conf.globalOptions.excl optNimV2
|
||||
conf.globalOptions.excl {optTinyRtti, optOwnedRefs, optSeqDestructors}
|
||||
conf.selectedGC = gcUnselected
|
||||
|
||||
var m = graph.makeModule(scriptName)
|
||||
@@ -229,8 +229,8 @@ proc runNimScript*(cache: IdentCache; scriptName: AbsoluteFile;
|
||||
|
||||
# watch out, "newruntime" can be set within NimScript itself and then we need
|
||||
# to remember this:
|
||||
if optNimV2 in oldGlobalOptions:
|
||||
conf.globalOptions.incl optNimV2
|
||||
if optOwnedRefs in oldGlobalOptions:
|
||||
conf.globalOptions.incl {optTinyRtti, optOwnedRefs, optSeqDestructors}
|
||||
defineSymbol(conf.symbols, "nimv2")
|
||||
if conf.selectedGC == gcUnselected:
|
||||
conf.selectedGC = oldSelectedGC
|
||||
|
||||
@@ -1724,7 +1724,7 @@ proc semReturn(c: PContext, n: PNode): PNode =
|
||||
result = n
|
||||
checkSonsLen(n, 1, c.config)
|
||||
if c.p.owner.kind in {skConverter, skMethod, skProc, skFunc, skMacro} or
|
||||
isClosureIterator(c.p.owner):
|
||||
isClosureIterator(c.p.owner.typ):
|
||||
if n.sons[0].kind != nkEmpty:
|
||||
# transform ``return expr`` to ``result = expr; return``
|
||||
if c.p.resultSym != nil:
|
||||
@@ -1771,7 +1771,7 @@ proc semProcBody(c: PContext, n: PNode): PNode =
|
||||
else:
|
||||
localError(c.config, c.p.resultSym.info, errCannotInferReturnType %
|
||||
c.p.owner.name.s)
|
||||
if isInlineIterator(c.p.owner) and c.p.owner.typ.sons[0] != nil and
|
||||
if isInlineIterator(c.p.owner.typ) and c.p.owner.typ.sons[0] != nil and
|
||||
c.p.owner.typ.sons[0].kind == tyUntyped:
|
||||
localError(c.config, c.p.owner.info, errCannotInferReturnType %
|
||||
c.p.owner.name.s)
|
||||
@@ -2563,7 +2563,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||
# if isGenericRoutine(result.sym):
|
||||
# localError(c.config, n.info, errInstantiateXExplicitly, s.name.s)
|
||||
# "procs literals" are 'owned'
|
||||
if optNimV2 in c.config.globalOptions:
|
||||
if optOwnedRefs in c.config.globalOptions:
|
||||
result.typ = makeVarType(c, result.typ, tyOwned)
|
||||
else:
|
||||
result = semSym(c, n, s, flags)
|
||||
|
||||
@@ -380,7 +380,7 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||
t = skipTypes(t, {tyGenericInst, tyAlias, tySink, tyOwned})
|
||||
if t.kind == tyRef:
|
||||
t = skipTypes(t.sons[0], {tyGenericInst, tyAlias, tySink, tyOwned})
|
||||
if optNimV2 in c.config.globalOptions:
|
||||
if optOwnedRefs in c.config.globalOptions:
|
||||
result.typ = makeVarType(c, result.typ, tyOwned)
|
||||
# we have to watch out, there are also 'owned proc' types that can be used
|
||||
# multiple times as long as they don't have closures.
|
||||
|
||||
@@ -38,7 +38,6 @@ const
|
||||
errPragmaOnlyInHeaderOfProcX = "pragmas are only allowed in the header of a proc; redefinition of $1"
|
||||
errCannotAssignMacroSymbol = "cannot assign macro symbol to $1 here. Forgot to invoke the macro with '()'?"
|
||||
errInvalidTypeDescAssign = "'typedesc' metatype is not valid here; typed '=' instead of ':'?"
|
||||
errInlineIteratorNotFirstClass = "inline iterators are not first-class / cannot be assigned to variables"
|
||||
|
||||
proc semDiscard(c: PContext, n: PNode): PNode =
|
||||
result = n
|
||||
@@ -456,9 +455,6 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
|
||||
if def.sym.kind == skMacro:
|
||||
localError(c.config, def.info, errCannotAssignMacroSymbol % "variable")
|
||||
def.typ = errorType(c)
|
||||
elif isInlineIterator(def.sym):
|
||||
localError(c.config, def.info, errInlineIteratorNotFirstClass)
|
||||
def.typ = errorType(c)
|
||||
elif def.typ.kind == tyTypeDesc and c.p.owner.kind != skMacro:
|
||||
# prevent the all too common 'var x = int' bug:
|
||||
localError(c.config, def.info, errInvalidTypeDescAssign)
|
||||
@@ -601,9 +597,6 @@ proc semConst(c: PContext, n: PNode): PNode =
|
||||
if def.sym.kind == skMacro:
|
||||
localError(c.config, def.info, errCannotAssignMacroSymbol % "constant")
|
||||
def.typ = errorType(c)
|
||||
elif isInlineIterator(def.sym):
|
||||
localError(c.config, def.info, errInlineIteratorNotFirstClass)
|
||||
def.typ = errorType(c)
|
||||
elif def.typ.kind == tyTypeDesc and c.p.owner.kind != skMacro:
|
||||
# prevent the all too common 'const x = int' bug:
|
||||
localError(c.config, def.info, errInvalidTypeDescAssign)
|
||||
@@ -1526,7 +1519,7 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||
closeScope(c) # close scope for parameters
|
||||
popOwner(c)
|
||||
result.typ = s.typ
|
||||
if optNimV2 in c.config.globalOptions:
|
||||
if optOwnedRefs in c.config.globalOptions:
|
||||
result.typ = makeVarType(c, result.typ, tyOwned)
|
||||
|
||||
proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode =
|
||||
@@ -1563,7 +1556,7 @@ proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode =
|
||||
popProcCon(c)
|
||||
popOwner(c)
|
||||
closeScope(c)
|
||||
if optNimV2 in c.config.globalOptions and result.typ != nil:
|
||||
if optOwnedRefs in c.config.globalOptions and result.typ != nil:
|
||||
result.typ = makeVarType(c, result.typ, tyOwned)
|
||||
# alternative variant (not quite working):
|
||||
# var prc = arg[0].sym
|
||||
@@ -1589,7 +1582,7 @@ proc maybeAddResult(c: PContext, s: PSym, n: PNode) =
|
||||
let resultType = sysTypeFromName(c.graph, n.info, "NimNode")
|
||||
addResult(c, resultType, n.info, s.kind)
|
||||
addResultNode(c, n)
|
||||
elif s.typ.sons[0] != nil and not isInlineIterator(s):
|
||||
elif s.typ.sons[0] != nil and not isInlineIterator(s.typ):
|
||||
addResult(c, s.typ.sons[0], n.info, s.kind)
|
||||
addResultNode(c, n)
|
||||
|
||||
@@ -1955,7 +1948,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
|
||||
if isAnon:
|
||||
n.kind = nkLambda
|
||||
result.typ = s.typ
|
||||
if optNimV2 in c.config.globalOptions:
|
||||
if optOwnedRefs in c.config.globalOptions:
|
||||
result.typ = makeVarType(c, result.typ, tyOwned)
|
||||
if isTopLevel(c) and s.kind != skIterator and
|
||||
s.typ.callConv == ccClosure:
|
||||
@@ -1969,7 +1962,6 @@ proc determineType(c: PContext, s: PSym) =
|
||||
|
||||
proc semIterator(c: PContext, n: PNode): PNode =
|
||||
# gensym'ed iterator?
|
||||
let isAnon = n[namePos].kind == nkEmpty
|
||||
if n[namePos].kind == nkSym:
|
||||
# gensym'ed iterators might need to become closure iterators:
|
||||
n[namePos].sym.owner = getCurrOwner(c)
|
||||
@@ -1983,8 +1975,6 @@ proc semIterator(c: PContext, n: PNode): PNode =
|
||||
var t = s.typ
|
||||
if t.sons[0] == nil and s.typ.callConv != ccClosure:
|
||||
localError(c.config, n.info, "iterator needs a return type")
|
||||
if isAnon and s.typ.callConv == ccInline:
|
||||
localError(c.config, n.info, errInlineIteratorNotFirstClass)
|
||||
# iterators are either 'inline' or 'closure'; for backwards compatibility,
|
||||
# we require first class iterators to be marked with 'closure' explicitly
|
||||
# -- at least for 0.9.2.
|
||||
|
||||
@@ -635,6 +635,12 @@ proc semRecordCase(c: PContext, n: PNode, check: var IntSet, pos: var int,
|
||||
errorUndeclaredIdentifier(c, n.sons[0].info, typ.sym.name.s)
|
||||
elif not isOrdinalType(typ):
|
||||
localError(c.config, n.sons[0].info, "selector must be of an ordinal type, float or string")
|
||||
elif firstOrd(c.config, typ) != 0:
|
||||
localError(c.config, n.info, "low(" & $a.sons[0].sym.name.s &
|
||||
") must be 0 for discriminant")
|
||||
elif lengthOrd(c.config, typ) > 0x00007FFF:
|
||||
localError(c.config, n.info, "len($1) must be less than 32768" % a.sons[0].sym.name.s)
|
||||
|
||||
for i in 1 ..< len(n):
|
||||
var b = copyTree(n.sons[i])
|
||||
addSon(a, b)
|
||||
@@ -871,7 +877,7 @@ proc semAnyRef(c: PContext; n: PNode; kind: TTypeKind; prev: PType): PType =
|
||||
if tfPartial in result.flags:
|
||||
if result.lastSon.kind == tyObject: incl(result.lastSon.flags, tfPartial)
|
||||
#if not isNilable: result.flags.incl tfNotNil
|
||||
if isOwned and optNimV2 in c.config.globalOptions:
|
||||
if isOwned and optOwnedRefs in c.config.globalOptions:
|
||||
let t = newTypeS(tyOwned, c)
|
||||
t.flags.incl tfHasOwned
|
||||
t.rawAddSonNoPropagationOfTypeFlags result
|
||||
@@ -1638,7 +1644,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||
result = semTypeof(c, n[1], prev)
|
||||
elif op.s == "typeof" and n[0].kind == nkSym and n[0].sym.magic == mTypeOf:
|
||||
result = semTypeof2(c, n, prev)
|
||||
elif op.s == "owned" and optNimV2 notin c.config.globalOptions and n.len == 2:
|
||||
elif op.s == "owned" and optOwnedRefs notin c.config.globalOptions and n.len == 2:
|
||||
result = semTypeExpr(c, n[1], prev)
|
||||
else:
|
||||
if c.inGenericContext > 0 and n.kind == nkCall:
|
||||
@@ -1662,26 +1668,9 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||
of mSet: result = semSet(c, n, prev)
|
||||
of mOrdinal: result = semOrdinal(c, n, prev)
|
||||
of mSeq:
|
||||
if false: # c.config.selectedGC == gcDestructors and optNimV2 notin c.config.globalOptions:
|
||||
let s = c.graph.sysTypes[tySequence]
|
||||
assert s != nil
|
||||
assert prev == nil
|
||||
result = copyType(s, s.owner, keepId=false)
|
||||
# Remove the 'T' parameter from tySequence:
|
||||
result.sons.setLen 0
|
||||
result.n = nil
|
||||
result.flags = {tfHasAsgn}
|
||||
semContainerArg(c, n, "seq", result)
|
||||
if result.len > 0:
|
||||
var base = result[0]
|
||||
if base.kind in {tyGenericInst, tyAlias, tySink}: base = lastSon(base)
|
||||
if not containsGenericType(base):
|
||||
# base.kind != tyGenericParam:
|
||||
c.typesWithOps.add((result, result))
|
||||
else:
|
||||
result = semContainer(c, n, tySequence, "seq", prev)
|
||||
if c.config.selectedGC == gcDestructors:
|
||||
incl result.flags, tfHasAsgn
|
||||
result = semContainer(c, n, tySequence, "seq", prev)
|
||||
if optSeqDestructors in c.config.globalOptions:
|
||||
incl result.flags, tfHasAsgn
|
||||
of mOpt: result = semContainer(c, n, tyOpt, "opt", prev)
|
||||
of mVarargs: result = semVarargs(c, n, prev)
|
||||
of mTypeDesc, mType, mTypeOf:
|
||||
@@ -1856,7 +1845,7 @@ proc processMagicType(c: PContext, m: PSym) =
|
||||
of mString:
|
||||
setMagicType(c.config, m, tyString, szUncomputedSize)
|
||||
rawAddSon(m.typ, getSysType(c.graph, m.info, tyChar))
|
||||
if c.config.selectedGC == gcDestructors:
|
||||
if optSeqDestructors in c.config.globalOptions:
|
||||
incl m.typ.flags, tfHasAsgn
|
||||
of mCstring:
|
||||
setMagicIntegral(c.config, m, tyCString, c.config.target.ptrSize)
|
||||
@@ -1897,7 +1886,7 @@ proc processMagicType(c: PContext, m: PSym) =
|
||||
setMagicIntegral(c.config, m, tyUncheckedArray, szUncomputedSize)
|
||||
of mSeq:
|
||||
setMagicType(c.config, m, tySequence, szUncomputedSize)
|
||||
if c.config.selectedGC == gcDestructors:
|
||||
if optSeqDestructors in c.config.globalOptions:
|
||||
incl m.typ.flags, tfHasAsgn
|
||||
assert c.graph.sysTypes[tySequence] == nil
|
||||
c.graph.sysTypes[tySequence] = m.typ
|
||||
|
||||
@@ -2590,11 +2590,6 @@ proc instTypeBoundOp*(c: PContext; dc: PSym; t: PType; info: TLineInfo;
|
||||
if f.kind in {tyRef, tyPtr}: f = f.lastSon
|
||||
else:
|
||||
if f.kind == tyVar: f = f.lastSon
|
||||
#if c.config.selectedGC == gcDestructors and f.kind == tySequence:
|
||||
# use the canonical type to access the =sink and =destroy etc.
|
||||
# f = c.graph.sysTypes[tySequence]
|
||||
#echo "YUP_---------Formal ", typeToString(f, preferDesc), " real ", typeToString(t, preferDesc), " ", f.id, " ", t.id
|
||||
|
||||
if typeRel(m, f, t) == isNone:
|
||||
localError(c.config, info, "cannot instantiate: '" & dc.name.s & "'")
|
||||
else:
|
||||
|
||||
@@ -231,7 +231,7 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) =
|
||||
typ.size = conf.target.ptrSize
|
||||
typ.align = int16(conf.target.ptrSize)
|
||||
of tyString:
|
||||
if conf.selectedGC == gcDestructors:
|
||||
if optSeqDestructors in conf.globalOptions:
|
||||
typ.size = conf.target.ptrSize * 2
|
||||
else:
|
||||
typ.size = conf.target.ptrSize
|
||||
@@ -245,7 +245,7 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) =
|
||||
typ.paddingAtEnd = szIllegalRecursion
|
||||
return
|
||||
typ.align = int16(conf.target.ptrSize)
|
||||
if typ.kind == tySequence and conf.selectedGC == gcDestructors:
|
||||
if typ.kind == tySequence and optSeqDestructors in conf.globalOptions:
|
||||
typ.size = conf.target.ptrSize * 2
|
||||
else:
|
||||
typ.size = conf.target.ptrSize
|
||||
|
||||
@@ -65,7 +65,7 @@ proc addLocalVar(g: ModuleGraph; varSection, varInit: PNode; owner: PSym; typ: P
|
||||
vpart.sons[2] = if varInit.isNil: v else: vpart[1]
|
||||
varSection.add vpart
|
||||
if varInit != nil:
|
||||
if useShallowCopy and typeNeedsNoDeepCopy(typ) or optNimV2 in g.config.globalOptions:
|
||||
if useShallowCopy and typeNeedsNoDeepCopy(typ) or optTinyRtti in g.config.globalOptions:
|
||||
varInit.add newFastAsgnStmt(newSymNode(result), v)
|
||||
else:
|
||||
let deepCopyCall = newNodeI(nkCall, varInit.info, 3)
|
||||
|
||||
@@ -914,7 +914,7 @@ proc transform(c: PTransf, n: PNode): PTransNode =
|
||||
oldDeferAnchor = c.deferAnchor
|
||||
c.deferAnchor = n
|
||||
if (n.typ != nil and tfHasAsgn in n.typ.flags) or
|
||||
optNimV2 in c.graph.config.globalOptions:
|
||||
optSeqDestructors in c.graph.config.globalOptions:
|
||||
c.needsDestroyPass = true
|
||||
case n.kind
|
||||
of nkSym:
|
||||
|
||||
@@ -1121,9 +1121,16 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
|
||||
result = sameChildrenAux(a, b, c) and sameFlags(a, b)
|
||||
if result and {ExactGenericParams, ExactTypeDescValues} * c.flags != {}:
|
||||
result = a.sym.position == b.sym.position
|
||||
of tyGenericInvocation, tyGenericBody, tySequence,
|
||||
tyOpenArray, tySet, tyRef, tyPtr, tyVar, tyLent, tySink, tyUncheckedArray,
|
||||
tyArray, tyProc, tyVarargs, tyOrdinal, tyTypeClasses, tyOpt, tyOwned:
|
||||
of tyBuiltInTypeClass:
|
||||
assert a.len == 1
|
||||
assert a[0].len == 0
|
||||
assert b.len == 1
|
||||
assert b[0].len == 0
|
||||
result = a[0].kind == b[0].kind
|
||||
of tyGenericInvocation, tyGenericBody, tySequence, tyOpenArray, tySet, tyRef,
|
||||
tyPtr, tyVar, tyLent, tySink, tyUncheckedArray, tyArray, tyProc, tyVarargs,
|
||||
tyOrdinal, tyCompositeTypeClass, tyUserTypeClass, tyUserTypeClassInst,
|
||||
tyAnd, tyOr, tyNot, tyAnything, tyOpt, tyOwned:
|
||||
cycleCheck()
|
||||
if a.kind == tyUserTypeClass and a.n != nil: return a.n == b.n
|
||||
result = sameChildrenAux(a, b, c)
|
||||
@@ -1280,10 +1287,13 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
|
||||
if kind notin {skParam, skResult}: result = t
|
||||
else: result = typeAllowedAux(marker, t2, kind, flags)
|
||||
of tyProc:
|
||||
if isInlineIterator(typ) and kind in {skVar, skLet, skConst, skParam, skResult}:
|
||||
# only closure iterators my be assigned to anything.
|
||||
result = t
|
||||
let f = if kind in {skProc, skFunc}: flags+{taNoUntyped} else: flags
|
||||
for i in 1 ..< len(t):
|
||||
result = typeAllowedAux(marker, t.sons[i], skParam, f-{taIsOpenArray})
|
||||
if result != nil: break
|
||||
result = typeAllowedAux(marker, t.sons[i], skParam, f-{taIsOpenArray})
|
||||
if result.isNil and t.sons[0] != nil:
|
||||
result = typeAllowedAux(marker, t.sons[0], skResult, flags)
|
||||
of tyTypeDesc:
|
||||
|
||||
@@ -710,7 +710,6 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||
of rkNode:
|
||||
if regs[ra].node.kind == nkNilLit:
|
||||
stackTrace(c, tos, pc, errNilAccess)
|
||||
assert nfIsRef in regs[ra].node.flags
|
||||
regs[ra].node[] = regs[rc].regToNode[]
|
||||
regs[ra].node.flags.incl nfIsRef
|
||||
else: stackTrace(c, tos, pc, errNilAccess)
|
||||
|
||||
@@ -948,9 +948,13 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
|
||||
c.genAddSubInt(n, dest, opcAddInt)
|
||||
of mInc, mDec:
|
||||
unused(c, n, dest)
|
||||
let opc = if m == mInc: opcAddInt else: opcSubInt
|
||||
let isUnsigned = n.sons[1].typ.skipTypes(abstractVarRange).kind in {tyUInt..tyUInt64}
|
||||
let opc = if not isUnsigned:
|
||||
if m == mInc: opcAddInt else: opcSubInt
|
||||
else:
|
||||
if m == mInc: opcAddu else: opcSubu
|
||||
let d = c.genx(n.sons[1])
|
||||
if n.sons[2].isInt8Lit:
|
||||
if n.sons[2].isInt8Lit and not isUnsigned:
|
||||
c.gABI(n, succ(opc), d, d, n.sons[2].intVal)
|
||||
else:
|
||||
let tmp = c.genx(n.sons[2])
|
||||
|
||||
@@ -207,15 +207,15 @@ clang.objc.options.linker = "-lobjc -lgnustep-base"
|
||||
gcc.options.linker %= "-L $WIND_BASE/target/lib/usr/lib/ppc/PPC32/common -mrtp -fno-strict-aliasing -D_C99 -D_HAS_C9X -std=c99 -fasm -Wall -Wno-write-strings"
|
||||
@end
|
||||
|
||||
gcc.options.speed = "-O3 -fno-strict-aliasing"
|
||||
gcc.options.size = "-Os"
|
||||
gcc.options.speed = "-O3 -fno-strict-aliasing -fno-ident"
|
||||
gcc.options.size = "-Os -fno-ident"
|
||||
@if windows:
|
||||
gcc.options.debug = "-g3 -Og -gdwarf-3"
|
||||
@else:
|
||||
gcc.options.debug = "-g3 -Og"
|
||||
@end
|
||||
gcc.cpp.options.speed = "-O3 -fno-strict-aliasing"
|
||||
gcc.cpp.options.size = "-Os"
|
||||
gcc.cpp.options.speed = "-O3 -fno-strict-aliasing -fno-ident"
|
||||
gcc.cpp.options.size = "-Os -fno-ident"
|
||||
gcc.cpp.options.debug = "-g3 -Og"
|
||||
#passl = "-pg"
|
||||
|
||||
@@ -257,7 +257,8 @@ vcc.linkerexe = "vccexe.exe"
|
||||
vcc.cpp.linkerexe = "vccexe.exe"
|
||||
|
||||
vcc.cpp.options.always = "/EHsc"
|
||||
vcc.options.linker.always = "/F33554432" # set the stack size to 32 MiB
|
||||
vcc.options.linker = "/F33554432" # set the stack size to 32 MiB
|
||||
vcc.cpp.options.linker = "/F33554432"
|
||||
vcc.options.debug = "/Zi /FS /Od"
|
||||
vcc.cpp.options.debug = "/Zi /FS /Od"
|
||||
vcc.options.speed = "/O2"
|
||||
|
||||
@@ -89,6 +89,8 @@ Advanced options:
|
||||
strings is allowed; only for backwards compatibility
|
||||
--nilseqs:on|off allow 'nil' for strings/seqs for
|
||||
backwards compatibility
|
||||
--seqsv2:on|off use the new string/seq implementation based on
|
||||
destructors
|
||||
--oldast:on|off use old AST for backwards compatibility
|
||||
--skipCfg:on|off do not read the nim installation's configuration file
|
||||
--skipUserCfg:on|off do not read the user's configuration file
|
||||
|
||||
30
doc/gc.rst
30
doc/gc.rst
@@ -119,9 +119,12 @@ procs ``GC_ref`` and ``GC_unref`` to mark objects as referenced to avoid them
|
||||
being freed by the GC. Other useful procs from `system <system.html>`_ you can
|
||||
use to keep track of memory are:
|
||||
|
||||
* getTotalMem(): returns the amount of total memory managed by the GC.
|
||||
* getOccupiedMem(): bytes reserved by the GC and used by objects.
|
||||
* getFreeMem(): bytes reserved by the GC and not in use.
|
||||
* ``getTotalMem()`` Returns the amount of total memory managed by the GC.
|
||||
* ``getOccupiedMem()`` Bytes reserved by the GC and used by objects.
|
||||
* ``getFreeMem()`` Bytes reserved by the GC and not in use.
|
||||
|
||||
These numbers are usually only for the running thread, not for the whole heap,
|
||||
with the exception of ``--gc:boehm`` and ``--gc:go``.
|
||||
|
||||
In addition to ``GC_ref`` and ``GC_unref`` you can avoid the GC by manually
|
||||
allocating memory with procs like ``alloc``, ``allocShared``, or
|
||||
@@ -144,3 +147,24 @@ The numbers count the number of objects in all GC heaps, they refer to
|
||||
all running threads, not only to the current thread. (The current thread
|
||||
would be the thread that calls ``dumpNumberOfInstances``.) This might
|
||||
change in later versions.
|
||||
|
||||
|
||||
Garbage collector options
|
||||
-------------------------
|
||||
|
||||
You can choose which garbage collector to use when compiling source code,
|
||||
you can pass ``--gc:`` on the compile command with the choosed garbage collector.
|
||||
|
||||
- ``--gc:refc`` Deferred `reference counting <https://en.wikipedia.org/wiki/Reference_counting>`_ with cycle detection, `thread local heap <https://en.wikipedia.org/wiki/Heap_(programming)>`_, default.
|
||||
- ``--gc:markAndSweep`` `Mark-And-Sweep <https://en.wikipedia.org/wiki/Tracing_garbage_collection#Copying_vs._mark-and-sweep_vs._mark-and-don't-sweep>`_ based garbage collector, `thread local heap <https://en.wikipedia.org/wiki/Heap_(programming)>`_.
|
||||
- ``--gc:boehm`` `Boehm <https://en.wikipedia.org/wiki/Boehm_garbage_collector>`_ based garbage collector, `stop-the-world <https://en.wikipedia.org/wiki/Tracing_garbage_collection#Stop-the-world_vs._incremental_vs._concurrent>`_, `shared heap <https://en.wikipedia.org/wiki/Heap_(programming)>`_.
|
||||
- ``--gc:go`` Go lang like garbage collector, `stop-the-world <https://en.wikipedia.org/wiki/Tracing_garbage_collection#Stop-the-world_vs._incremental_vs._concurrent>`_, `shared heap <https://en.wikipedia.org/wiki/Heap_(programming)>`_.
|
||||
- ``--gc:regions`` `Stack <https://en.wikipedia.org/wiki/Memory_management#Stack_allocation>`_ based garbage collector.
|
||||
- ``--gc:none`` No garbage collector.
|
||||
|
||||
The same Nim code can be compiled to use any of the garbage collectors;
|
||||
the Nim syntax generally will not change from one garbage collector to another.
|
||||
No garbage collector is used for `JavaScript and NodeJS <https://nim-lang.github.io/Nim/backends.html#backends-the-javascript-target>`_ compilation targets.
|
||||
`NimScript <https://nim-lang.github.io/Nim/nims.html>`_ target uses Nim VM garbage collector.
|
||||
|
||||
If you are new to Nim and just starting, the default garbage collector is balanced to fit most common use cases.
|
||||
|
||||
@@ -3511,8 +3511,8 @@ for details on how to change this behavior.
|
||||
Anonymous Procs
|
||||
---------------
|
||||
|
||||
Procs can also be treated as expressions, in which case it's allowed to omit
|
||||
the proc's name.
|
||||
Unnamed procedures can be used as lambda expressions to pass into other
|
||||
procedures:
|
||||
|
||||
.. code-block:: nim
|
||||
var cities = @["Frankfurt", "Tokyo", "New York", "Kyiv"]
|
||||
@@ -3522,7 +3522,9 @@ the proc's name.
|
||||
|
||||
|
||||
Procs as expressions can appear both as nested procs and inside top level
|
||||
executable code.
|
||||
executable code. The `sugar <sugar.html>`_ module contains the `=>` macro
|
||||
which enables a more succinct syntax for anonymous procedures resembling
|
||||
lambdas as they are in languages like JavaScript, C#, etc.
|
||||
|
||||
|
||||
Func
|
||||
@@ -3694,7 +3696,7 @@ type.
|
||||
|
||||
method eval(e: Expression): int {.base.} =
|
||||
# override this base method
|
||||
quit "to override!"
|
||||
raise newException(CatchableError, "Method without implementation override")
|
||||
|
||||
method eval(e: Literal): int = return e.x
|
||||
|
||||
@@ -6222,6 +6224,25 @@ but are used to override the settings temporarily. Example:
|
||||
# ... some code ...
|
||||
{.pop.} # restore old settings
|
||||
|
||||
`push/pop`:idx: can switch on/off some standard library pragmas, example:
|
||||
|
||||
.. code-block:: nim
|
||||
{.push inline.}
|
||||
proc thisIsInlined(): int = 42
|
||||
func willBeInlined(): float = 42.0
|
||||
{.pop.}
|
||||
proc notInlined(): int = 9
|
||||
|
||||
{.push discardable, boundChecks: off, compileTime, noSideEffect, experimental.}
|
||||
template example(): string = "https://nim-lang.org"
|
||||
{.pop.}
|
||||
|
||||
{.push deprecated, hint[LineTooLong]: off, used, stackTrace: off.}
|
||||
proc sample(): bool = true
|
||||
{.pop.}
|
||||
|
||||
For third party pragmas it depends on its implementation, but uses the same syntax.
|
||||
|
||||
|
||||
register pragma
|
||||
---------------
|
||||
|
||||
@@ -142,15 +142,6 @@ comments can also be nested.
|
||||
]#
|
||||
]#
|
||||
|
||||
You can also use the `discard statement <#procedures-discard-statement>`_ together with *long string
|
||||
literals* to create block comments:
|
||||
|
||||
.. code-block:: nim
|
||||
:test: "nim c $1"
|
||||
discard """ You can have any Nim code text commented
|
||||
out inside this with no indentation restrictions.
|
||||
yes("May I ask a pointless question?") """
|
||||
|
||||
|
||||
Numbers
|
||||
-------
|
||||
|
||||
4
koch.nim
4
koch.nim
@@ -320,8 +320,8 @@ proc boot(args: string) =
|
||||
# jsonbuild then uses the $project.json file to build the Nim binary.
|
||||
exec "$# $# $# $# --nimcache:$# --compileOnly compiler" / "nim.nim" %
|
||||
[nimi, bootOptions, extraOption, args, smartNimcache]
|
||||
exec "$# jsonscript --nimcache:$# compiler" / "nim.nim" %
|
||||
[nimi, smartNimcache]
|
||||
exec "$# jsonscript $# --nimcache:$# compiler" / "nim.nim" %
|
||||
[nimi, args, smartNimcache]
|
||||
|
||||
if sameFileContent(output, i.thVersion):
|
||||
copyExe(output, finalDest)
|
||||
|
||||
@@ -94,6 +94,18 @@
|
||||
##
|
||||
## db.close()
|
||||
##
|
||||
##
|
||||
## Note
|
||||
## ====
|
||||
## This module does not implement any ORM features such as mapping the types from the schema.
|
||||
## Instead, a ``seq[string]`` is returned for each row.
|
||||
##
|
||||
## The reasoning is as follows:
|
||||
## 1. it's close to what many DBs offer natively (char**)
|
||||
## 2. it hides the number of types that the DB supports
|
||||
## (int? int64? decimal up to 10 places? geo coords?)
|
||||
## 3. it's convenient when all you do is to forward the data to somewhere else (echo, log, put the data into a new query)
|
||||
##
|
||||
## See also
|
||||
## ========
|
||||
##
|
||||
|
||||
@@ -24,6 +24,7 @@ proc logImpl(console: Console) {.importcpp: "log", varargs.}
|
||||
proc debugImpl(console: Console) {.importcpp: "debug", varargs.}
|
||||
proc infoImpl(console: Console) {.importcpp: "info", varargs.}
|
||||
proc errorImpl(console: Console) {.importcpp: "error", varargs.}
|
||||
proc warnImpl(console: Console) {.importcpp: "warn", varargs.}
|
||||
|
||||
proc makeConsoleCall(console: NimNode, procName: NimNode, args: NimNode): NimNode =
|
||||
result = newCall(procName, console)
|
||||
@@ -41,4 +42,28 @@ macro info*(console: Console, args: varargs[RootRef, convertToConsoleLoggable]):
|
||||
macro error*(console: Console, args: varargs[RootRef, convertToConsoleLoggable]): untyped =
|
||||
makeConsoleCall(console, bindSym "errorImpl", args)
|
||||
|
||||
var console* {.importc, nodecl.}: Console
|
||||
|
||||
macro warn*(console: Console, args: varargs[RootRef, convertToConsoleLoggable]): untyped =
|
||||
## https://developer.mozilla.org/en-US/docs/Web/API/Console/warn
|
||||
makeConsoleCall(console, bindSym "warnImpl", args)
|
||||
|
||||
proc clear*(console: Console) {.importcpp: "clear".} ## https://developer.mozilla.org/en-US/docs/Web/API/Console/clear
|
||||
|
||||
proc count*(console: Console, label = "".cstring) {.importcpp: "count".} ## https://developer.mozilla.org/en-US/docs/Web/API/Console/count
|
||||
|
||||
proc countReset*(console: Console, label = "".cstring) {.importcpp: "countReset".} ## https://developer.mozilla.org/en-US/docs/Web/API/Console/countReset
|
||||
|
||||
proc group*(console: Console, label = "".cstring) {.importcpp: "group".} ## https://developer.mozilla.org/en-US/docs/Web/API/Console/group
|
||||
|
||||
proc groupCollapsed*(console: Console, label = "".cstring) {.importcpp: "groupCollapsed".} ## https://developer.mozilla.org/en-US/docs/Web/API/Console/groupCollapsed
|
||||
|
||||
proc groupEnd*(console: Console) {.importcpp: "groupEnd".} ## https://developer.mozilla.org/en-US/docs/Web/API/Console/groupEnd
|
||||
|
||||
proc time*(console: Console, label = "".cstring) {.importcpp: "time".} ## https://developer.mozilla.org/en-US/docs/Web/API/Console/time
|
||||
|
||||
proc timeEnd*(console: Console, label = "".cstring) {.importcpp: "timeEnd".} ## https://developer.mozilla.org/en-US/docs/Web/API/Console/timeEnd
|
||||
|
||||
proc timeLog*(console: Console, label = "".cstring) {.importcpp: "timeLog".} ## https://developer.mozilla.org/en-US/docs/Web/API/Console/timeLog
|
||||
|
||||
|
||||
var console* {.importc, nodecl.}: Console
|
||||
|
||||
@@ -56,100 +56,119 @@
|
||||
|
||||
const
|
||||
cb64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
|
||||
invalidChar = 255
|
||||
|
||||
template encodeInternal(s: typed, lineLen: int, newLine: string): untyped =
|
||||
## encodes `s` into base64 representation. After `lineLen` characters, a
|
||||
## `newline` is added.
|
||||
var total = ((len(s) + 2) div 3) * 4
|
||||
let numLines = (total + lineLen - 1) div lineLen
|
||||
if numLines > 0: inc(total, (numLines - 1) * newLine.len)
|
||||
template encodeInternal(s: typed): untyped =
|
||||
## encodes `s` into base64 representation.
|
||||
proc encodeSize(size: int): int =
|
||||
return (size * 4 div 3) + 6
|
||||
|
||||
result.setLen(encodeSize(s.len))
|
||||
|
||||
result = newString(total)
|
||||
var
|
||||
i = 0
|
||||
r = 0
|
||||
currLine = 0
|
||||
while i < s.len - 2:
|
||||
let
|
||||
a = ord(s[i])
|
||||
b = ord(s[i+1])
|
||||
c = ord(s[i+2])
|
||||
result[r] = cb64[a shr 2]
|
||||
result[r+1] = cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)]
|
||||
result[r+2] = cb64[((b and 0x0F) shl 2) or ((c and 0xC0) shr 6)]
|
||||
result[r+3] = cb64[c and 0x3F]
|
||||
inc(r, 4)
|
||||
inc(i, 3)
|
||||
inc(currLine, 4)
|
||||
# avoid index out of bounds when lineLen == encoded length
|
||||
if currLine >= lineLen and i != s.len-2 and r < total:
|
||||
for x in items(newLine):
|
||||
result[r] = x
|
||||
inc(r)
|
||||
currLine = 0
|
||||
inputIndex = 0
|
||||
outputIndex = 0
|
||||
inputEnds = s.len - s.len mod 3
|
||||
n: uint32
|
||||
b: uint32
|
||||
|
||||
if i < s.len-1:
|
||||
let
|
||||
a = ord(s[i])
|
||||
b = ord(s[i+1])
|
||||
result[r] = cb64[a shr 2]
|
||||
result[r+1] = cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)]
|
||||
result[r+2] = cb64[((b and 0x0F) shl 2)]
|
||||
result[r+3] = '='
|
||||
if r+4 != result.len:
|
||||
setLen(result, r+4)
|
||||
elif i < s.len:
|
||||
let a = ord(s[i])
|
||||
result[r] = cb64[a shr 2]
|
||||
result[r+1] = cb64[(a and 3) shl 4]
|
||||
result[r+2] = '='
|
||||
result[r+3] = '='
|
||||
if r+4 != result.len:
|
||||
setLen(result, r+4)
|
||||
else:
|
||||
if r != result.len:
|
||||
setLen(result, r)
|
||||
#assert(r == result.len)
|
||||
discard
|
||||
template inputByte(exp: untyped) =
|
||||
b = uint32(s[inputIndex])
|
||||
n = exp
|
||||
inc inputIndex
|
||||
|
||||
proc encode*[T: SomeInteger|char](s: openArray[T], lineLen = 75,
|
||||
newLine = ""): string =
|
||||
## Encodes ``s`` into base64 representation. After ``lineLen`` characters, a
|
||||
## ``newline`` is added.
|
||||
template outputChar(x: untyped) =
|
||||
result[outputIndex] = cb64[x and 63]
|
||||
inc outputIndex
|
||||
|
||||
template outputChar(c: char) =
|
||||
result[outputIndex] = c
|
||||
inc outputIndex
|
||||
|
||||
while inputIndex != inputEnds:
|
||||
inputByte(b shl 16)
|
||||
inputByte(n or b shl 8)
|
||||
inputByte(n or b shl 0)
|
||||
outputChar(n shr 18)
|
||||
outputChar(n shr 12)
|
||||
outputChar(n shr 6)
|
||||
outputChar(n shr 0)
|
||||
|
||||
var padding = s.len mod 3
|
||||
if padding == 1:
|
||||
inputByte(b shl 16)
|
||||
outputChar(n shr 18)
|
||||
outputChar(n shr 12)
|
||||
outputChar('=')
|
||||
outputChar('=')
|
||||
|
||||
elif padding == 2:
|
||||
inputByte(b shl 16)
|
||||
inputByte(n or b shl 8)
|
||||
outputChar(n shr 18)
|
||||
outputChar(n shr 12)
|
||||
outputChar(n shr 6)
|
||||
outputChar('=')
|
||||
|
||||
result.setLen(outputIndex)
|
||||
|
||||
proc encode*[T: SomeInteger|char](s: openarray[T]): string =
|
||||
## Encodes `s` into base64 representation.
|
||||
##
|
||||
## This procedure encodes an openarray (array or sequence) of either integers
|
||||
## or characters.
|
||||
##
|
||||
## **See also:**
|
||||
## * `encode proc<#encode,string,int,string>`_ for encoding a string
|
||||
## * `encode proc<#encode,string>`_ for encoding a string
|
||||
## * `decode proc<#decode,string>`_ for decoding a string
|
||||
runnableExamples:
|
||||
assert encode(['n', 'i', 'm']) == "bmlt"
|
||||
assert encode(@['n', 'i', 'm']) == "bmlt"
|
||||
assert encode([1, 2, 3, 4, 5]) == "AQIDBAU="
|
||||
encodeInternal(s, lineLen, newLine)
|
||||
encodeInternal(s)
|
||||
|
||||
proc encode*(s: string, lineLen = 75, newLine = ""): string =
|
||||
## Encodes ``s`` into base64 representation. After ``lineLen`` characters, a
|
||||
## ``newline`` is added.
|
||||
proc encode*(s: string): string =
|
||||
## Encodes ``s`` into base64 representation.
|
||||
##
|
||||
## This procedure encodes a string.
|
||||
##
|
||||
## **See also:**
|
||||
## * `encode proc<#encode,openArray[T],int,string>`_ for encoding an openarray
|
||||
## * `encode proc<#encode,openArray[T]>`_ for encoding an openarray
|
||||
## * `decode proc<#decode,string>`_ for decoding a string
|
||||
runnableExamples:
|
||||
assert encode("Hello World") == "SGVsbG8gV29ybGQ="
|
||||
assert encode("Hello World", 3, "\n") == "SGVs\nbG8g\nV29ybGQ="
|
||||
encodeInternal(s, lineLen, newLine)
|
||||
encodeInternal(s)
|
||||
|
||||
proc decodeByte(b: char): int {.inline.} =
|
||||
case b
|
||||
of '+': result = ord('>')
|
||||
of '0'..'9': result = ord(b) + 4
|
||||
of 'A'..'Z': result = ord(b) - ord('A')
|
||||
of 'a'..'z': result = ord(b) - 71
|
||||
else: result = 63
|
||||
proc encodeMIME*(s: string, lineLen = 75, newLine = "\r\n"): string =
|
||||
## Encodes ``s`` into base64 representation as lines.
|
||||
## Used in email MIME forma, use ``lineLen`` and ``newline``.
|
||||
##
|
||||
## This procedure encodes a string according to MIME spec.
|
||||
##
|
||||
## **See also:**
|
||||
## * `encode proc<#encode,string>`_ for encoding a string
|
||||
## * `decode proc<#decode,string>`_ for decoding a string
|
||||
runnableExamples:
|
||||
assert encodeMIME("Hello World", 4, "\n") == "SGVs\nbG8g\nV29y\nbGQ="
|
||||
for i, c in encode(s):
|
||||
if i != 0 and (i mod lineLen == 0):
|
||||
result.add(newLine)
|
||||
result.add(c)
|
||||
|
||||
proc initDecodeTable*(): array[256, char] =
|
||||
# computes a decode table at compile time
|
||||
for i in 0 ..< 256:
|
||||
let ch = char(i)
|
||||
var code = invalidChar
|
||||
if ch >= 'A' and ch <= 'Z': code = i - 0x00000041
|
||||
if ch >= 'a' and ch <= 'z': code = i - 0x00000047
|
||||
if ch >= '0' and ch <= '9': code = i + 0x00000004
|
||||
if ch == '+' or ch == '-': code = 0x0000003E
|
||||
if ch == '/' or ch == '_': code = 0x0000003F
|
||||
result[i] = char(code)
|
||||
|
||||
const
|
||||
decodeTable = initDecodeTable()
|
||||
|
||||
proc decode*(s: string): string =
|
||||
## Decodes string ``s`` in base64 representation back into its original form.
|
||||
@@ -161,53 +180,58 @@ proc decode*(s: string): string =
|
||||
runnableExamples:
|
||||
assert decode("SGVsbG8gV29ybGQ=") == "Hello World"
|
||||
assert decode(" SGVsbG8gV29ybGQ=") == "Hello World"
|
||||
const Whitespace = {' ', '\t', '\v', '\r', '\l', '\f'}
|
||||
var total = ((len(s) + 3) div 4) * 3
|
||||
# total is an upper bound, as we will skip arbitrary whitespace:
|
||||
result = newString(total)
|
||||
if s.len == 0: return
|
||||
|
||||
proc decodeSize(size: int): int =
|
||||
return (size * 3 div 4) + 6
|
||||
|
||||
template inputChar(x: untyped) =
|
||||
let x = int decode_table[ord(s[inputIndex])]
|
||||
inc inputIndex
|
||||
if x == invalidChar:
|
||||
raise newException(ValueError,
|
||||
"Invalid base64 format character " & repr(s[inputIndex]) &
|
||||
" at location " & $inputIndex & ".")
|
||||
|
||||
template outputChar(x: untyped) =
|
||||
result[outputIndex] = char(x and 255)
|
||||
inc outputIndex
|
||||
|
||||
# pre allocate output string once
|
||||
result.setLen(decodeSize(s.len))
|
||||
var
|
||||
i = 0
|
||||
r = 0
|
||||
while true:
|
||||
while i < s.len and s[i] in Whitespace: inc(i)
|
||||
if i < s.len-3:
|
||||
let
|
||||
a = s[i].decodeByte
|
||||
b = s[i+1].decodeByte
|
||||
c = s[i+2].decodeByte
|
||||
d = s[i+3].decodeByte
|
||||
inputIndex = 0
|
||||
outputIndex = 0
|
||||
inputLen = s.len
|
||||
inputEnds = 0
|
||||
# strip trailing characters
|
||||
while s[inputLen - 1] in {'\n', '\r', ' ', '='}:
|
||||
dec inputLen
|
||||
# hot loop: read 4 characters at at time
|
||||
inputEnds = inputLen - 4
|
||||
while inputIndex <= inputEnds:
|
||||
while s[inputIndex] in {'\n', '\r', ' '}:
|
||||
inc inputIndex
|
||||
inputChar(a)
|
||||
inputChar(b)
|
||||
inputChar(c)
|
||||
inputChar(d)
|
||||
outputChar(a shl 2 or b shr 4)
|
||||
outputChar(b shl 4 or c shr 2)
|
||||
outputChar(c shl 6 or d shr 0)
|
||||
# do the last 2 or 3 characters
|
||||
var leftLen = abs((inputIndex - inputLen) mod 4)
|
||||
if leftLen == 2:
|
||||
inputChar(a)
|
||||
inputChar(b)
|
||||
outputChar(a shl 2 or b shr 4)
|
||||
elif leftLen == 3:
|
||||
inputChar(a)
|
||||
inputChar(b)
|
||||
inputChar(c)
|
||||
outputChar(a shl 2 or b shr 4)
|
||||
outputChar(b shl 4 or c shr 2)
|
||||
result.setLen(outputIndex)
|
||||
|
||||
result[r] = chr((a shl 2) and 0xff or ((b shr 4) and 0x03))
|
||||
result[r+1] = chr((b shl 4) and 0xff or ((c shr 2) and 0x0F))
|
||||
result[r+2] = chr((c shl 6) and 0xff or (d and 0x3F))
|
||||
inc(r, 3)
|
||||
inc(i, 4)
|
||||
else: break
|
||||
assert i == s.len
|
||||
# adjust the length:
|
||||
if i > 0 and s[i-1] == '=':
|
||||
dec(r)
|
||||
if i > 1 and s[i-2] == '=': dec(r)
|
||||
setLen(result, r)
|
||||
|
||||
when isMainModule:
|
||||
assert encode("leasure.") == "bGVhc3VyZS4="
|
||||
assert encode("easure.") == "ZWFzdXJlLg=="
|
||||
assert encode("asure.") == "YXN1cmUu"
|
||||
assert encode("sure.") == "c3VyZS4="
|
||||
|
||||
const testInputExpandsTo76 = "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++"
|
||||
const testInputExpands = "++++++++++++++++++++++++++++++"
|
||||
const longText = """Man is distinguished, not only by his reason, but by this
|
||||
singular passion from other animals, which is a lust of the mind,
|
||||
that by a perseverance of delight in the continued and indefatigable
|
||||
generation of knowledge, exceeds the short vehemence of any carnal
|
||||
pleasure."""
|
||||
const tests = ["", "abc", "xyz", "man", "leasure.", "sure.", "easure.",
|
||||
"asure.", longText, testInputExpandsTo76, testInputExpands]
|
||||
|
||||
for t in items(tests):
|
||||
assert decode(encode(t)) == t
|
||||
assert decode(encode(t, lineLen = 40)) == t
|
||||
assert decode(encode(t, lineLen = 76)) == t
|
||||
|
||||
@@ -387,6 +387,7 @@ proc assign*(dest: var IntSet, src: IntSet) =
|
||||
else:
|
||||
dest.counter = src.counter
|
||||
dest.max = src.max
|
||||
dest.elems = src.elems
|
||||
newSeq(dest.data, src.data.len)
|
||||
|
||||
var it = src.head
|
||||
@@ -653,3 +654,19 @@ when isMainModule:
|
||||
xs = toSeq(items(x))
|
||||
xs.sort(cmp[int])
|
||||
assert xs == @[1, 4, 7, 1001, 1056]
|
||||
|
||||
proc bug12366 =
|
||||
var
|
||||
x = initIntSet()
|
||||
y = initIntSet()
|
||||
n = 3584
|
||||
|
||||
for i in 0..n:
|
||||
x.incl(i)
|
||||
y.incl(i)
|
||||
|
||||
let z = symmetricDifference(x, y)
|
||||
doAssert z.len == 0
|
||||
doAssert $z == "{}"
|
||||
|
||||
bug12366()
|
||||
|
||||
@@ -1019,9 +1019,9 @@ when isMainModule and not defined(release):
|
||||
# --> {1, 3, 5}
|
||||
|
||||
block toSeqAndString:
|
||||
var a = toHashSet([2, 4, 5])
|
||||
var a = toHashSet([2, 7, 5])
|
||||
var b = initHashSet[int]()
|
||||
for x in [2, 4, 5]: b.incl(x)
|
||||
for x in [2, 7, 5]: b.incl(x)
|
||||
assert($a == $b)
|
||||
#echo a
|
||||
#echo toHashSet(["no", "esc'aping", "is \" provided"])
|
||||
|
||||
@@ -112,29 +112,32 @@ proc hash*[T: proc](x: T): Hash {.inline.} =
|
||||
else:
|
||||
result = hash(pointer(x))
|
||||
|
||||
const
|
||||
prime = uint(11)
|
||||
|
||||
proc hash*(x: int): Hash {.inline.} =
|
||||
## Efficient hashing of integers.
|
||||
result = x
|
||||
result = cast[Hash](cast[uint](x) * prime)
|
||||
|
||||
proc hash*(x: int64): Hash {.inline.} =
|
||||
## Efficient hashing of `int64` integers.
|
||||
result = cast[int](x)
|
||||
result = cast[Hash](cast[uint](x) * prime)
|
||||
|
||||
proc hash*(x: uint): Hash {.inline.} =
|
||||
## Efficient hashing of unsigned integers.
|
||||
result = cast[int](x)
|
||||
result = cast[Hash](x * prime)
|
||||
|
||||
proc hash*(x: uint64): Hash {.inline.} =
|
||||
## Efficient hashing of `uint64` integers.
|
||||
result = cast[int](x)
|
||||
result = cast[Hash](cast[uint](x) * prime)
|
||||
|
||||
proc hash*(x: char): Hash {.inline.} =
|
||||
## Efficient hashing of characters.
|
||||
result = ord(x)
|
||||
result = cast[Hash](cast[uint](ord(x)) * prime)
|
||||
|
||||
proc hash*[T: Ordinal](x: T): Hash {.inline.} =
|
||||
## Efficient hashing of other ordinal types (e.g. enums).
|
||||
result = ord(x)
|
||||
result = cast[Hash](cast[uint](ord(x)) * prime)
|
||||
|
||||
proc hash*(x: float): Hash {.inline.} =
|
||||
## Efficient hashing of floats.
|
||||
|
||||
@@ -10,21 +10,33 @@
|
||||
## Do yourself a favor and import the module
|
||||
## as ``from htmlgen import nil`` and then fully qualify the macros.
|
||||
##
|
||||
## *Note*: The Karax project (``nimble install karax``) has a better
|
||||
## way to achieve the same, see `https://github.com/pragmagic/karax/blob/master/tests/nativehtmlgen.nim`_
|
||||
## for an example.
|
||||
##
|
||||
##
|
||||
## This module implements a simple `XML`:idx: and `HTML`:idx: code
|
||||
## generator. Each commonly used HTML tag has a corresponding macro
|
||||
## that generates a string with its HTML representation.
|
||||
##
|
||||
## MathML
|
||||
## ======
|
||||
##
|
||||
## `MathML <https://wikipedia.org/wiki/MathML>`_ is supported, MathML is part of HTML5.
|
||||
## `MathML <https://wikipedia.org/wiki/MathML>`_ is an Standard ISO/IEC 40314 from year 2015.
|
||||
## MathML allows you to `draw advanced math on the web <https://developer.mozilla.org/en-US/docs/Web/MathML/Element/math#Examples>`_,
|
||||
## `visually similar to Latex math. <https://developer.mozilla.org/en-US/docs/Web/MathML/Element/semantics#Example>`_
|
||||
##
|
||||
## Examples
|
||||
## ========
|
||||
##
|
||||
## .. code-block:: Nim
|
||||
## var nim = "Nim"
|
||||
## echo h1(a(href="http://nim-lang.org", nim))
|
||||
## echo h1(a(href="https://nim-lang.org", nim))
|
||||
##
|
||||
## Writes the string::
|
||||
##
|
||||
## <h1><a href="http://nim-lang.org">Nim</a></h1>
|
||||
## <h1><a href="https://nim-lang.org">Nim</a></h1>
|
||||
##
|
||||
|
||||
import
|
||||
@@ -604,9 +616,195 @@ macro wbr*(e: varargs[untyped]): untyped =
|
||||
## generates the HTML ``wbr`` element.
|
||||
result = xmlCheckedTag(e, "wbr", commonAttr, "", true)
|
||||
|
||||
|
||||
macro math*(e: varargs[untyped]): untyped =
|
||||
## Generates the HTML ``math`` element. MathML https://wikipedia.org/wiki/MathML
|
||||
## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/math#Examples
|
||||
result = xmlCheckedTag(e, "math", "mathbackground mathcolor href overflow" & commonAttr)
|
||||
|
||||
macro maction*(e: varargs[untyped]): untyped =
|
||||
## Generates the HTML ``maction`` element. MathML https://wikipedia.org/wiki/MathML
|
||||
## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/maction
|
||||
result = xmlCheckedTag(e, "maction", "mathbackground mathcolor href" & commonAttr)
|
||||
|
||||
macro menclose*(e: varargs[untyped]): untyped =
|
||||
## Generates the HTML ``menclose`` element. MathML https://wikipedia.org/wiki/MathML
|
||||
## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/menclose
|
||||
result = xmlCheckedTag(e, "menclose", "mathbackground mathcolor href notation" & commonAttr)
|
||||
|
||||
macro merror*(e: varargs[untyped]): untyped =
|
||||
## Generates the HTML ``merror`` element. MathML https://wikipedia.org/wiki/MathML
|
||||
## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/merror
|
||||
result = xmlCheckedTag(e, "merror", "mathbackground mathcolor href" & commonAttr)
|
||||
|
||||
macro mfenced*(e: varargs[untyped]): untyped =
|
||||
## Generates the HTML ``mfenced`` element. MathML https://wikipedia.org/wiki/MathML
|
||||
## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mfenced
|
||||
result = xmlCheckedTag(e, "mfenced", "mathbackground mathcolor href open separators" & commonAttr)
|
||||
|
||||
macro mfrac*(e: varargs[untyped]): untyped =
|
||||
## Generates the HTML ``mfrac`` element. MathML https://wikipedia.org/wiki/MathML
|
||||
## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mfrac
|
||||
result = xmlCheckedTag(e, "mfrac", "mathbackground mathcolor href linethickness numalign" & commonAttr)
|
||||
|
||||
macro mglyph*(e: varargs[untyped]): untyped =
|
||||
## Generates the HTML ``mglyph`` element. MathML https://wikipedia.org/wiki/MathML
|
||||
## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mglyph
|
||||
result = xmlCheckedTag(e, "mglyph", "mathbackground mathcolor href src valign" & commonAttr)
|
||||
|
||||
macro mi*(e: varargs[untyped]): untyped =
|
||||
## Generates the HTML ``mi`` element. MathML https://wikipedia.org/wiki/MathML
|
||||
## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mi
|
||||
result = xmlCheckedTag(e, "mi", "mathbackground mathcolor href mathsize mathvariant" & commonAttr)
|
||||
|
||||
macro mlabeledtr*(e: varargs[untyped]): untyped =
|
||||
## Generates the HTML ``mlabeledtr`` element. MathML https://wikipedia.org/wiki/MathML
|
||||
## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mlabeledtr
|
||||
result = xmlCheckedTag(e, "mlabeledtr", "mathbackground mathcolor href columnalign groupalign rowalign" & commonAttr)
|
||||
|
||||
macro mmultiscripts*(e: varargs[untyped]): untyped =
|
||||
## Generates the HTML ``mmultiscripts`` element. MathML https://wikipedia.org/wiki/MathML
|
||||
## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mmultiscripts
|
||||
result = xmlCheckedTag(e, "mmultiscripts", "mathbackground mathcolor href subscriptshift superscriptshift" & commonAttr)
|
||||
|
||||
macro mn*(e: varargs[untyped]): untyped =
|
||||
## Generates the HTML ``mn`` element. MathML https://wikipedia.org/wiki/MathML
|
||||
## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mn
|
||||
result = xmlCheckedTag(e, "mn", "mathbackground mathcolor href mathsize mathvariant" & commonAttr)
|
||||
|
||||
macro mo*(e: varargs[untyped]): untyped =
|
||||
## Generates the HTML ``mo`` element. MathML https://wikipedia.org/wiki/MathML
|
||||
## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mo
|
||||
result = xmlCheckedTag(e, "mo",
|
||||
"mathbackground mathcolor fence form largeop lspace mathsize mathvariant movablelimits rspace separator stretchy symmetric" & commonAttr)
|
||||
|
||||
macro mover*(e: varargs[untyped]): untyped =
|
||||
## Generates the HTML ``mover`` element. MathML https://wikipedia.org/wiki/MathML
|
||||
## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mover
|
||||
result = xmlCheckedTag(e, "mover", "mathbackground mathcolor accent href" & commonAttr)
|
||||
|
||||
macro mpadded*(e: varargs[untyped]): untyped =
|
||||
## Generates the HTML ``mpadded`` element. MathML https://wikipedia.org/wiki/MathML
|
||||
## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mpadded
|
||||
result = xmlCheckedTag(e, "mpadded", "mathbackground mathcolor depth href lspace voffset" & commonAttr)
|
||||
|
||||
macro mphantom*(e: varargs[untyped]): untyped =
|
||||
## Generates the HTML ``mphantom`` element. MathML https://wikipedia.org/wiki/MathML
|
||||
## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mphantom
|
||||
result = xmlCheckedTag(e, "mphantom", "mathbackground" & commonAttr)
|
||||
|
||||
macro mroot*(e: varargs[untyped]): untyped =
|
||||
## Generates the HTML ``mroot`` element. MathML https://wikipedia.org/wiki/MathML
|
||||
## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mroot
|
||||
result = xmlCheckedTag(e, "mroot", "mathbackground mathcolor href" & commonAttr)
|
||||
|
||||
macro mrow*(e: varargs[untyped]): untyped =
|
||||
## Generates the HTML ``mrow`` element. MathML https://wikipedia.org/wiki/MathML
|
||||
## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mrow
|
||||
result = xmlCheckedTag(e, "mrow", "mathbackground mathcolor href" & commonAttr)
|
||||
|
||||
macro ms*(e: varargs[untyped]): untyped =
|
||||
## Generates the HTML ``ms`` element. MathML https://wikipedia.org/wiki/MathML
|
||||
## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/ms
|
||||
result = xmlCheckedTag(e, "ms", "mathbackground mathcolor href lquote mathsize mathvariant rquote" & commonAttr)
|
||||
|
||||
macro mspace*(e: varargs[untyped]): untyped =
|
||||
## Generates the HTML ``mspace`` element. MathML https://wikipedia.org/wiki/MathML
|
||||
## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mspace
|
||||
result = xmlCheckedTag(e, "mspace", "mathbackground mathcolor href linebreak" & commonAttr)
|
||||
|
||||
macro msqrt*(e: varargs[untyped]): untyped =
|
||||
## Generates the HTML ``msqrt`` element. MathML https://wikipedia.org/wiki/MathML
|
||||
## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/msqrt
|
||||
result = xmlCheckedTag(e, "msqrt", "mathbackground mathcolor href" & commonAttr)
|
||||
|
||||
macro mstyle*(e: varargs[untyped]): untyped =
|
||||
## Generates the HTML ``mstyle`` element. MathML https://wikipedia.org/wiki/MathML
|
||||
## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mstyle
|
||||
result = xmlCheckedTag(e, "mstyle", ("mathbackground mathcolor href decimalpoint displaystyle " &
|
||||
"infixlinebreakstyle scriptlevel scriptminsize scriptsizemultiplier" & commonAttr))
|
||||
|
||||
macro msub*(e: varargs[untyped]): untyped =
|
||||
## Generates the HTML ``msub`` element. MathML https://wikipedia.org/wiki/MathML
|
||||
## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/msub
|
||||
result = xmlCheckedTag(e, "msub", "mathbackground mathcolor href subscriptshift" & commonAttr)
|
||||
|
||||
macro msubsup*(e: varargs[untyped]): untyped =
|
||||
## Generates the HTML ``msubsup`` element. MathML https://wikipedia.org/wiki/MathML
|
||||
## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/msubsup
|
||||
result = xmlCheckedTag(e, "msubsup", "mathbackground mathcolor href subscriptshift superscriptshift" & commonAttr)
|
||||
|
||||
macro msup*(e: varargs[untyped]): untyped =
|
||||
## Generates the HTML ``msup`` element. MathML https://wikipedia.org/wiki/MathML
|
||||
## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/msup
|
||||
result = xmlCheckedTag(e, "msup", "mathbackground mathcolor href superscriptshift" & commonAttr)
|
||||
|
||||
macro mtable*(e: varargs[untyped]): untyped =
|
||||
## Generates the HTML ``mtable`` element. MathML https://wikipedia.org/wiki/MathML
|
||||
## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mtable
|
||||
result = xmlCheckedTag(e, "mtable", ("mathbackground mathcolor href align " &
|
||||
"alignmentscope columnalign columnlines columnspacing columnwidth " &
|
||||
"displaystyle equalcolumns equalrows frame framespacing groupalign " &
|
||||
"rowalign rowlines rowspacing side width" & commonAttr))
|
||||
|
||||
macro mtd*(e: varargs[untyped]): untyped =
|
||||
## Generates the HTML ``mtd`` element. MathML https://wikipedia.org/wiki/MathML
|
||||
## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mtd
|
||||
result = xmlCheckedTag(e, "mtd",
|
||||
"mathbackground mathcolor href columnalign columnspan groupalign rowalign rowspan" & commonAttr)
|
||||
|
||||
macro mtext*(e: varargs[untyped]): untyped =
|
||||
## Generates the HTML ``mtext`` element. MathML https://wikipedia.org/wiki/MathML
|
||||
## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mtext
|
||||
result = xmlCheckedTag(e, "mtext", "mathbackground mathcolor href mathsize mathvariant" & commonAttr)
|
||||
|
||||
macro munder*(e: varargs[untyped]): untyped =
|
||||
## Generates the HTML ``munder`` element. MathML https://wikipedia.org/wiki/MathML
|
||||
## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/munder
|
||||
result = xmlCheckedTag(e, "munder", "mathbackground mathcolor href accentunder align" & commonAttr)
|
||||
|
||||
macro munderover*(e: varargs[untyped]): untyped =
|
||||
## Generates the HTML ``munderover`` element. MathML https://wikipedia.org/wiki/MathML
|
||||
## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/munderover
|
||||
result = xmlCheckedTag(e, "munderover", "mathbackground mathcolor href accentunder accent align" & commonAttr)
|
||||
|
||||
macro semantics*(e: varargs[untyped]): untyped =
|
||||
## Generates the HTML ``semantics`` element. MathML https://wikipedia.org/wiki/MathML
|
||||
## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/semantics
|
||||
result = xmlCheckedTag(e, "semantics", "mathbackground mathcolor href definitionURL encoding cd src" & commonAttr)
|
||||
|
||||
macro annotation*(e: varargs[untyped]): untyped =
|
||||
## Generates the HTML ``annotation`` element. MathML https://wikipedia.org/wiki/MathML
|
||||
## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/semantics
|
||||
result = xmlCheckedTag(e, "annotation", "mathbackground mathcolor href definitionURL encoding cd src" & commonAttr)
|
||||
|
||||
macro `annotation-xml`*(e: varargs[untyped]): untyped =
|
||||
## Generates the HTML ``annotation-xml`` element. MathML https://wikipedia.org/wiki/MathML
|
||||
## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/semantics
|
||||
result = xmlCheckedTag(e, "annotation", "mathbackground mathcolor href definitionURL encoding cd src" & commonAttr)
|
||||
|
||||
|
||||
runnableExamples:
|
||||
let nim = "Nim"
|
||||
assert h1(a(href = "http://nim-lang.org", nim)) ==
|
||||
"""<h1><a href="http://nim-lang.org">Nim</a></h1>"""
|
||||
assert h1(a(href = "https://nim-lang.org", nim)) ==
|
||||
"""<h1><a href="https://nim-lang.org">Nim</a></h1>"""
|
||||
assert form(action = "test", `accept-charset` = "Content-Type") ==
|
||||
"""<form action="test" accept-charset="Content-Type"></form>"""
|
||||
|
||||
|
||||
assert math(
|
||||
semantics(
|
||||
mrow(
|
||||
msup(
|
||||
mi("x"),
|
||||
mn("42")
|
||||
)
|
||||
)
|
||||
)
|
||||
) == "<math><semantics><mrow><msup><mi>x</mi><mn>42</mn></msup></mrow></semantics></math>"
|
||||
|
||||
assert math(
|
||||
semantics(
|
||||
annotation(encoding = "application/x-tex", title = "Latex on Web", r"x^{2} + y")
|
||||
)
|
||||
) == """<math><semantics><annotation encoding="application/x-tex" title="Latex on Web">x^{2} + y</annotation></semantics></math>"""
|
||||
|
||||
@@ -454,7 +454,7 @@ proc generateHeaders(requestUrl: Uri, httpMethod: string,
|
||||
|
||||
# Proxy auth header.
|
||||
if not proxy.isNil and proxy.auth != "":
|
||||
let auth = base64.encode(proxy.auth, newline = "")
|
||||
let auth = base64.encode(proxy.auth)
|
||||
add(result, "Proxy-Authorization: basic " & auth & "\c\L")
|
||||
|
||||
for key, val in headers:
|
||||
|
||||
@@ -833,50 +833,37 @@ proc parseJson(p: var JsonParser): JsonNode =
|
||||
of tkError, tkCurlyRi, tkBracketRi, tkColon, tkComma, tkEof:
|
||||
raiseParseErr(p, "{")
|
||||
|
||||
when not defined(js):
|
||||
iterator parseJsonFragments*(s: Stream, filename: string = ""): JsonNode =
|
||||
## Parses from a stream `s` into `JsonNodes`. `filename` is only needed
|
||||
## for nice error messages.
|
||||
## The JSON fragments are separated by whitespace. This can be substantially
|
||||
## faster than the comparable loop
|
||||
## ``for x in splitWhitespace(s): yield parseJson(x)``.
|
||||
## This closes the stream `s` after it's done.
|
||||
var p: JsonParser
|
||||
p.open(s, filename)
|
||||
try:
|
||||
discard getTok(p) # read first token
|
||||
while p.tok != tkEof:
|
||||
yield p.parseJson()
|
||||
finally:
|
||||
p.close()
|
||||
iterator parseJsonFragments*(s: Stream, filename: string = ""): JsonNode =
|
||||
## Parses from a stream `s` into `JsonNodes`. `filename` is only needed
|
||||
## for nice error messages.
|
||||
## The JSON fragments are separated by whitespace. This can be substantially
|
||||
## faster than the comparable loop
|
||||
## ``for x in splitWhitespace(s): yield parseJson(x)``.
|
||||
## This closes the stream `s` after it's done.
|
||||
var p: JsonParser
|
||||
p.open(s, filename)
|
||||
try:
|
||||
discard getTok(p) # read first token
|
||||
while p.tok != tkEof:
|
||||
yield p.parseJson()
|
||||
finally:
|
||||
p.close()
|
||||
|
||||
proc parseJson*(s: Stream, filename: string = ""): JsonNode =
|
||||
## Parses from a stream `s` into a `JsonNode`. `filename` is only needed
|
||||
## for nice error messages.
|
||||
## If `s` contains extra data, it will raise `JsonParsingError`.
|
||||
## This closes the stream `s` after it's done.
|
||||
var p: JsonParser
|
||||
p.open(s, filename)
|
||||
try:
|
||||
discard getTok(p) # read first token
|
||||
result = p.parseJson()
|
||||
eat(p, tkEof) # check if there is no extra data
|
||||
finally:
|
||||
p.close()
|
||||
proc parseJson*(s: Stream, filename: string = ""): JsonNode =
|
||||
## Parses from a stream `s` into a `JsonNode`. `filename` is only needed
|
||||
## for nice error messages.
|
||||
## If `s` contains extra data, it will raise `JsonParsingError`.
|
||||
## This closes the stream `s` after it's done.
|
||||
var p: JsonParser
|
||||
p.open(s, filename)
|
||||
try:
|
||||
discard getTok(p) # read first token
|
||||
result = p.parseJson()
|
||||
eat(p, tkEof) # check if there is no extra data
|
||||
finally:
|
||||
p.close()
|
||||
|
||||
proc parseJson*(buffer: string): JsonNode =
|
||||
## Parses JSON from `buffer`.
|
||||
## If `buffer` contains extra data, it will raise `JsonParsingError`.
|
||||
result = parseJson(newStringStream(buffer), "input")
|
||||
|
||||
proc parseFile*(filename: string): JsonNode =
|
||||
## Parses `file` into a `JsonNode`.
|
||||
## If `file` contains extra data, it will raise `JsonParsingError`.
|
||||
var stream = newFileStream(filename, fmRead)
|
||||
if stream == nil:
|
||||
raise newException(IOError, "cannot read from file: " & filename)
|
||||
result = parseJson(stream, filename)
|
||||
else:
|
||||
when defined(js):
|
||||
from math import `mod`
|
||||
type
|
||||
JSObject = object
|
||||
@@ -946,38 +933,32 @@ else:
|
||||
result = newJNull()
|
||||
|
||||
proc parseJson*(buffer: string): JsonNode =
|
||||
return parseNativeJson(buffer).convertObject()
|
||||
when nimvm:
|
||||
return parseJson(newStringStream(buffer), "input")
|
||||
else:
|
||||
return parseNativeJson(buffer).convertObject()
|
||||
|
||||
# -- Json deserialiser macro. --
|
||||
else:
|
||||
proc parseJson*(buffer: string): JsonNode =
|
||||
## Parses JSON from `buffer`.
|
||||
## If `buffer` contains extra data, it will raise `JsonParsingError`.
|
||||
result = parseJson(newStringStream(buffer), "input")
|
||||
|
||||
proc createJsonIndexer(jsonNode: NimNode,
|
||||
index: string | int | NimNode): NimNode
|
||||
{.compileTime.} =
|
||||
when index is string:
|
||||
let indexNode = newStrLitNode(index)
|
||||
elif index is int:
|
||||
let indexNode = newIntLitNode(index)
|
||||
elif index is NimNode:
|
||||
let indexNode = index
|
||||
proc parseFile*(filename: string): JsonNode =
|
||||
## Parses `file` into a `JsonNode`.
|
||||
## If `file` contains extra data, it will raise `JsonParsingError`.
|
||||
var stream = newFileStream(filename, fmRead)
|
||||
if stream == nil:
|
||||
raise newException(IOError, "cannot read from file: " & filename)
|
||||
result = parseJson(stream, filename)
|
||||
|
||||
result = newNimNode(nnkBracketExpr).add(
|
||||
jsonNode,
|
||||
indexNode
|
||||
)
|
||||
|
||||
proc transformJsonIndexer(jsonNode: NimNode): NimNode =
|
||||
case jsonNode.kind
|
||||
of nnkBracketExpr:
|
||||
result = newNimNode(nnkCurlyExpr)
|
||||
else:
|
||||
result = jsonNode.copy()
|
||||
|
||||
for child in jsonNode:
|
||||
result.add(transformJsonIndexer(child))
|
||||
# -- Json deserialiser. --
|
||||
|
||||
template verifyJsonKind(node: JsonNode, kinds: set[JsonNodeKind],
|
||||
ast: string) =
|
||||
if node.kind notin kinds:
|
||||
if node == nil:
|
||||
raise newException(KeyError, "key not found: " & ast)
|
||||
elif node.kind notin kinds:
|
||||
let msg = "Incorrect JSON kind. Wanted '$1' in '$2' but got '$3'." % [
|
||||
$kinds,
|
||||
ast,
|
||||
@@ -985,588 +966,232 @@ template verifyJsonKind(node: JsonNode, kinds: set[JsonNodeKind],
|
||||
]
|
||||
raise newException(JsonKindError, msg)
|
||||
|
||||
proc getEnum(node: JsonNode, ast: string, T: typedesc): T =
|
||||
when T is SomeInteger:
|
||||
# TODO: I shouldn't need this proc.
|
||||
proc convert[T](x: BiggestInt): T = T(x)
|
||||
verifyJsonKind(node, {JInt}, ast)
|
||||
return convert[T](node.getBiggestInt())
|
||||
else:
|
||||
verifyJsonKind(node, {JString}, ast)
|
||||
return parseEnum[T](node.getStr())
|
||||
|
||||
proc toIdentNode(typeNode: NimNode): NimNode =
|
||||
## Converts a Sym type node (returned by getType et al.) into an
|
||||
## Ident node. Placing Sym type nodes inside the resulting code AST is
|
||||
## unsound (according to @Araq) so this is necessary.
|
||||
case typeNode.kind
|
||||
of nnkSym:
|
||||
return newIdentNode($typeNode)
|
||||
of nnkBracketExpr:
|
||||
result = typeNode
|
||||
for i in 0..<len(result):
|
||||
result[i] = newIdentNode($result[i])
|
||||
of nnkIdent:
|
||||
return typeNode
|
||||
else:
|
||||
doAssert false, "Cannot convert typeNode to an ident node: " & $typeNode.kind
|
||||
when defined(nimFixedForwardGeneric):
|
||||
# The following forward declarations don't work in older versions of Nim
|
||||
|
||||
proc createGetEnumCall(jsonNode, kindType: NimNode): NimNode =
|
||||
# -> getEnum(`jsonNode`, `kindType`)
|
||||
result = newCall(bindSym("getEnum"), jsonNode, toStrLit(jsonNode), kindType)
|
||||
# forward declare all initFromJson
|
||||
|
||||
proc createOfBranchCond(ofBranch, getEnumCall: NimNode): NimNode =
|
||||
## Creates an expression that acts as the condition for an ``of`` branch.
|
||||
var cond = newIdentNode("false")
|
||||
for ofCond in ofBranch:
|
||||
if ofCond.kind == nnkRecList:
|
||||
break
|
||||
proc initFromJson(dst: var string; jsonNode: JsonNode; jsonPath: string)
|
||||
proc initFromJson(dst: var bool; jsonNode: JsonNode; jsonPath: string)
|
||||
proc initFromJson(dst: var JsonNode; jsonNode: JsonNode; jsonPath: string)
|
||||
proc initFromJson[T: SomeInteger](dst: var T; jsonNode: JsonNode, jsonPath: string)
|
||||
proc initFromJson[T: SomeFloat](dst: var T; jsonNode: JsonNode; jsonPath: string)
|
||||
proc initFromJson[T: enum](dst: var T; jsonNode: JsonNode; jsonPath: string)
|
||||
proc initFromJson[T](dst: var seq[T]; jsonNode: JsonNode; jsonPath: string)
|
||||
proc initFromJson[S,T](dst: var array[S,T]; jsonNode: JsonNode; jsonPath: string)
|
||||
proc initFromJson[T](dst: var Table[string,T];jsonNode: JsonNode; jsonPath: string)
|
||||
proc initFromJson[T](dst: var OrderedTable[string,T];jsonNode: JsonNode; jsonPath: string)
|
||||
proc initFromJson[T](dst: var ref T; jsonNode: JsonNode; jsonPath: string)
|
||||
proc initFromJson[T](dst: var Option[T]; jsonNode: JsonNode; jsonPath: string)
|
||||
proc initFromJson[T: distinct](dst: var T;jsonNode: JsonNode; jsonPath: string)
|
||||
proc initFromJson[T: object|tuple](dst: var T; jsonNode: JsonNode; jsonPath: string)
|
||||
|
||||
let comparison = infix(getEnumCall, "==", ofCond)
|
||||
cond = infix(cond, "or", comparison)
|
||||
# initFromJson definitions
|
||||
|
||||
return cond
|
||||
|
||||
proc processObjField(field, jsonNode: NimNode): seq[NimNode] {.compileTime.}
|
||||
proc processOfBranch(ofBranch, jsonNode, kindType,
|
||||
kindJsonNode: NimNode): seq[NimNode] {.compileTime.} =
|
||||
## Processes each field inside of an object's ``of`` branch.
|
||||
## For each field a new ExprColonExpr node is created and put in the
|
||||
## resulting list.
|
||||
##
|
||||
## Sample ``ofBranch`` AST:
|
||||
##
|
||||
## .. code-block::plain
|
||||
## OfBranch of 0, 1:
|
||||
## IntLit 0 foodPos: float
|
||||
## IntLit 1 enemyPos: float
|
||||
## RecList
|
||||
## Sym "foodPos"
|
||||
## Sym "enemyPos"
|
||||
result = @[]
|
||||
let getEnumCall = createGetEnumCall(kindJsonNode, kindType)
|
||||
|
||||
for branchField in ofBranch[^1]:
|
||||
let objFields = processObjField(branchField, jsonNode)
|
||||
|
||||
for objField in objFields:
|
||||
let exprColonExpr = newNimNode(nnkExprColonExpr)
|
||||
result.add(exprColonExpr)
|
||||
# Add the name of the field.
|
||||
exprColonExpr.add(toIdentNode(objField[0]))
|
||||
|
||||
# Add the value of the field.
|
||||
let cond = createOfBranchCond(ofBranch, getEnumCall)
|
||||
exprColonExpr.add(newIfStmt(
|
||||
(cond, objField[1])
|
||||
))
|
||||
|
||||
proc processElseBranch(recCaseNode, elseBranch, jsonNode, kindType,
|
||||
kindJsonNode: NimNode): seq[NimNode] {.compileTime.} =
|
||||
## Processes each field inside of a variant object's ``else`` branch.
|
||||
##
|
||||
## ..code-block::plain
|
||||
## Else
|
||||
## RecList
|
||||
## Sym "other"
|
||||
result = @[]
|
||||
let getEnumCall = createGetEnumCall(kindJsonNode, kindType)
|
||||
|
||||
# We need to build up a list of conditions from each ``of`` branch so that
|
||||
# we can then negate it to get ``else``.
|
||||
var cond = newIdentNode("false")
|
||||
for i in 1 ..< len(recCaseNode):
|
||||
if recCaseNode[i].kind == nnkElse:
|
||||
break
|
||||
|
||||
cond = infix(cond, "or", createOfBranchCond(recCaseNode[i], getEnumCall))
|
||||
|
||||
# Negate the condition.
|
||||
cond = prefix(cond, "not")
|
||||
|
||||
for branchField in elseBranch[^1]:
|
||||
let objFields = processObjField(branchField, jsonNode)
|
||||
|
||||
for objField in objFields:
|
||||
let exprColonExpr = newNimNode(nnkExprColonExpr)
|
||||
result.add(exprColonExpr)
|
||||
# Add the name of the field.
|
||||
exprColonExpr.add(toIdentNode(objField[0]))
|
||||
|
||||
# Add the value of the field.
|
||||
let ifStmt = newIfStmt((cond, objField[1]))
|
||||
exprColonExpr.add(ifStmt)
|
||||
|
||||
proc createConstructor(typeSym, jsonNode: NimNode): NimNode {.compileTime.}
|
||||
|
||||
proc detectDistinctType(typeSym: NimNode): NimNode =
|
||||
let
|
||||
typeImpl = getTypeImpl(typeSym)
|
||||
typeInst = getTypeInst(typeSym)
|
||||
result = if typeImpl.typeKind == ntyDistinct: typeImpl else: typeInst
|
||||
|
||||
proc processObjField(field, jsonNode: NimNode): seq[NimNode] =
|
||||
## Process a field from a ``RecList``.
|
||||
##
|
||||
## The field will typically be a simple ``Sym`` node, but for object variants
|
||||
## it may also be a ``RecCase`` in which case things become complicated.
|
||||
result = @[]
|
||||
case field.kind
|
||||
of nnkSym:
|
||||
# Ordinary field. For example, `name: string`.
|
||||
let exprColonExpr = newNimNode(nnkExprColonExpr)
|
||||
result.add(exprColonExpr)
|
||||
|
||||
# Add the field name.
|
||||
exprColonExpr.add(toIdentNode(field))
|
||||
|
||||
# Add the field value.
|
||||
# -> jsonNode["`field`"]
|
||||
let indexedJsonNode = createJsonIndexer(jsonNode, $field)
|
||||
let typeNode = detectDistinctType(field)
|
||||
exprColonExpr.add(createConstructor(typeNode, indexedJsonNode))
|
||||
of nnkRecCase:
|
||||
# A "case" field that introduces a variant.
|
||||
let exprEqExpr = newNimNode(nnkExprEqExpr)
|
||||
result.add(exprEqExpr)
|
||||
|
||||
# Add the "case" field name (usually "kind").
|
||||
exprEqExpr.add(toIdentNode(field[0]))
|
||||
|
||||
# -> jsonNode["`field[0]`"]
|
||||
let kindJsonNode = createJsonIndexer(jsonNode, $field[0])
|
||||
|
||||
# Add the "case" field's value.
|
||||
let kindType = toIdentNode(getTypeInst(field[0]))
|
||||
let getEnumSym = bindSym("getEnum")
|
||||
let astStrLit = toStrLit(kindJsonNode)
|
||||
let getEnumCall = newCall(getEnumSym, kindJsonNode, astStrLit, kindType)
|
||||
exprEqExpr.add(getEnumCall)
|
||||
|
||||
# Iterate through each `of` branch.
|
||||
for i in 1 ..< field.len:
|
||||
case field[i].kind
|
||||
of nnkOfBranch:
|
||||
result.add processOfBranch(field[i], jsonNode, kindType, kindJsonNode)
|
||||
of nnkElse:
|
||||
result.add processElseBranch(field, field[i], jsonNode, kindType, kindJsonNode)
|
||||
else:
|
||||
doAssert false, "Expected OfBranch or Else node kinds, got: " & $field[i].kind
|
||||
else:
|
||||
doAssert false, "Unable to process object field: " & $field.kind
|
||||
|
||||
doAssert result.len > 0
|
||||
|
||||
proc processFields(obj: NimNode,
|
||||
jsonNode: NimNode): seq[NimNode] {.compileTime.} =
|
||||
## Process all the fields of an ``ObjectTy`` and any of its
|
||||
## parent type's fields (via inheritance).
|
||||
result = @[]
|
||||
case obj.kind
|
||||
of nnkObjectTy:
|
||||
expectKind(obj[2], nnkRecList)
|
||||
for field in obj[2]:
|
||||
let nodes = processObjField(field, jsonNode)
|
||||
result.add(nodes)
|
||||
|
||||
# process parent type fields
|
||||
case obj[1].kind
|
||||
of nnkBracketExpr:
|
||||
assert $obj[1][0] == "ref"
|
||||
result.add(processFields(getType(obj[1][1]), jsonNode))
|
||||
of nnkSym:
|
||||
result.add(processFields(getType(obj[1]), jsonNode))
|
||||
proc initFromJson(dst: var string; jsonNode: JsonNode; jsonPath: string) =
|
||||
verifyJsonKind(jsonNode, {JString, JNull}, jsonPath)
|
||||
# since strings don't have a nil state anymore, this mapping of
|
||||
# JNull to the default string is questionable. `none(string)` and
|
||||
# `some("")` have the same potentional json value `JNull`.
|
||||
if jsonNode.kind == JNull:
|
||||
dst = ""
|
||||
else:
|
||||
dst = jsonNode.str
|
||||
|
||||
proc initFromJson(dst: var bool; jsonNode: JsonNode; jsonPath: string) =
|
||||
verifyJsonKind(jsonNode, {JBool}, jsonPath)
|
||||
dst = jsonNode.bval
|
||||
|
||||
proc initFromJson(dst: var JsonNode; jsonNode: JsonNode; jsonPath: string) =
|
||||
dst = jsonNode.copy
|
||||
|
||||
proc initFromJson[T: SomeInteger](dst: var T; jsonNode: JsonNode, jsonPath: string) =
|
||||
verifyJsonKind(jsonNode, {JInt}, jsonPath)
|
||||
dst = T(jsonNode.num)
|
||||
|
||||
proc initFromJson[T: SomeFloat](dst: var T; jsonNode: JsonNode; jsonPath: string) =
|
||||
verifyJsonKind(jsonNode, {JInt, JFloat}, jsonPath)
|
||||
if jsonNode.kind == JFloat:
|
||||
dst = T(jsonNode.fnum)
|
||||
else:
|
||||
dst = T(jsonNode.num)
|
||||
|
||||
proc initFromJson[T: enum](dst: var T; jsonNode: JsonNode; jsonPath: string) =
|
||||
verifyJsonKind(jsonNode, {JString}, jsonPath)
|
||||
dst = parseEnum[T](jsonNode.getStr)
|
||||
|
||||
proc initFromJson[T](dst: var seq[T]; jsonNode: JsonNode; jsonPath: string) =
|
||||
verifyJsonKind(jsonNode, {JArray}, jsonPath)
|
||||
dst.setLen jsonNode.len
|
||||
for i in 0 ..< jsonNode.len:
|
||||
initFromJson(dst[i], jsonNode[i], jsonPath & "[" & $i & "]")
|
||||
|
||||
proc initFromJson[S,T](dst: var array[S,T]; jsonNode: JsonNode; jsonPath: string) =
|
||||
verifyJsonKind(jsonNode, {JArray}, jsonPath)
|
||||
for i in 0 ..< jsonNode.len:
|
||||
initFromJson(dst[i], jsonNode[i], jsonPath & "[" & $i & "]")
|
||||
|
||||
proc initFromJson[T](dst: var Table[string,T];jsonNode: JsonNode; jsonPath: string) =
|
||||
dst = initTable[string, T]()
|
||||
verifyJsonKind(jsonNode, {JObject}, jsonPath)
|
||||
for key in keys(jsonNode.fields):
|
||||
initFromJson(mgetOrPut(dst, key, default(T)), jsonNode[key], jsonPath & "." & key)
|
||||
|
||||
proc initFromJson[T](dst: var OrderedTable[string,T];jsonNode: JsonNode; jsonPath: string) =
|
||||
dst = initOrderedTable[string,T]()
|
||||
verifyJsonKind(jsonNode, {JObject}, jsonPath)
|
||||
for key in keys(jsonNode.fields):
|
||||
initFromJson(mgetOrPut(dst, key, default(T)), jsonNode[key], jsonPath & "." & key)
|
||||
|
||||
proc initFromJson[T](dst: var ref T; jsonNode: JsonNode; jsonPath: string) =
|
||||
if jsonNode.kind == JNull:
|
||||
dst = nil
|
||||
else:
|
||||
dst = new(ref T)
|
||||
initFromJson(dst[], jsonNode, jsonPath)
|
||||
|
||||
proc initFromJson[T](dst: var Option[T]; jsonNode: JsonNode; jsonPath: string) =
|
||||
if jsonNode != nil and jsonNode.kind != JNull:
|
||||
dst = some(default(T))
|
||||
initFromJson(dst.get, jsonNode, jsonPath)
|
||||
|
||||
macro assignDistinctImpl[T : distinct](dst: var T;jsonNode: JsonNode; jsonPath: string) =
|
||||
let typInst = getTypeInst(dst)
|
||||
let typImpl = getTypeImpl(dst)
|
||||
let baseTyp = typImpl[0]
|
||||
result = quote do:
|
||||
initFromJson( `baseTyp`(`dst`), `jsonNode`, `jsonPath`)
|
||||
|
||||
proc initFromJson[T : distinct](dst: var T; jsonNode: JsonNode; jsonPath: string) =
|
||||
assignDistinctImpl(dst, jsonNode, jsonPath)
|
||||
|
||||
proc detectIncompatibleType(typeExpr, lineinfoNode: NimNode): void =
|
||||
if typeExpr.kind == nnkTupleConstr:
|
||||
error("Use a named tuple instead of: " & typeExpr.repr, lineinfoNode)
|
||||
|
||||
proc foldObjectBody(dst, typeNode, tmpSym, jsonNode, jsonPath: NimNode, depth: int): void {.compileTime.} =
|
||||
if depth > 150:
|
||||
error("recursion limit reached", typeNode)
|
||||
case typeNode.kind
|
||||
of nnkEmpty:
|
||||
discard
|
||||
of nnkTupleTy:
|
||||
for identDefs in obj:
|
||||
expectKind(identDefs, nnkIdentDefs)
|
||||
let nodes = processObjField(identDefs[0], jsonNode)
|
||||
result.add(nodes)
|
||||
else:
|
||||
doAssert false, "Unable to process field type: " & $obj.kind
|
||||
of nnkRecList, nnkTupleTy:
|
||||
for it in typeNode:
|
||||
foldObjectBody(dst, it, tmpSym, jsonNode, jsonPath, depth + 1)
|
||||
|
||||
proc processType(typeName: NimNode, obj: NimNode,
|
||||
jsonNode: NimNode, isRef: bool): NimNode {.compileTime.} =
|
||||
## Process a type such as ``Sym "float"`` or ``ObjectTy ...``.
|
||||
##
|
||||
## Sample ``ObjectTy``:
|
||||
##
|
||||
## .. code-block::plain
|
||||
## ObjectTy
|
||||
## Empty
|
||||
## InheritanceInformation
|
||||
## RecList
|
||||
## Sym "events"
|
||||
case obj.kind
|
||||
of nnkObjectTy, nnkTupleTy:
|
||||
# Create object constructor.
|
||||
result =
|
||||
if obj.kind == nnkObjectTy: newNimNode(nnkObjConstr)
|
||||
else: newNimNode(nnkPar)
|
||||
of nnkIdentDefs:
|
||||
typeNode.expectLen 3
|
||||
let fieldSym = typeNode[0]
|
||||
let fieldNameLit = newLit(fieldSym.strVal)
|
||||
let fieldType = typeNode[1]
|
||||
|
||||
if obj.kind == nnkObjectTy:
|
||||
result.add(typeName) # Name of the type to construct.
|
||||
# Detecting incompatiple tuple types in `assignObjectImpl` only
|
||||
# would be much cleaner, but the ast for tuple types does not
|
||||
# contain usable type information.
|
||||
detectIncompatibleType(fieldType, fieldSym)
|
||||
|
||||
# Process each object/tuple field and add it as an exprColonExpr
|
||||
result.add(processFields(obj, jsonNode))
|
||||
dst.add quote do:
|
||||
initFromJson(`tmpSym`.`fieldSym`, getOrDefault(`jsonNode`,`fieldNameLit`), `jsonPath` & "." & `fieldNameLit`)
|
||||
|
||||
# Object might be null. So we need to check for that.
|
||||
if isRef:
|
||||
result = quote do:
|
||||
verifyJsonKind(`jsonNode`, {JObject, JNull}, astToStr(`jsonNode`))
|
||||
if `jsonNode`.kind == JNull:
|
||||
nil
|
||||
of nnkRecCase:
|
||||
let kindSym = typeNode[0][0]
|
||||
let kindNameLit = newLit(kindSym.strVal)
|
||||
let kindType = typeNode[0][1]
|
||||
let kindOffsetLit = newLit(uint(getOffset(kindSym)))
|
||||
dst.add quote do:
|
||||
var kindTmp: `kindType`
|
||||
initFromJson(kindTmp, `jsonNode`[`kindNameLit`], `jsonPath` & "." & `kindNameLit`)
|
||||
when defined js:
|
||||
`tmpSym`.`kindSym` = kindTmp
|
||||
else:
|
||||
`result`
|
||||
when nimVm:
|
||||
`tmpSym`.`kindSym` = kindTmp
|
||||
else:
|
||||
# fuck it, assign kind field anyway
|
||||
((cast[ptr `kindType`](cast[uint](`tmpSym`.addr) + `kindOffsetLit`))[]) = kindTmp
|
||||
dst.add nnkCaseStmt.newTree(nnkDotExpr.newTree(tmpSym, kindSym))
|
||||
for i in 1 ..< typeNode.len:
|
||||
foldObjectBody(dst, typeNode[i], tmpSym, jsonNode, jsonPath, depth + 1)
|
||||
|
||||
of nnkOfBranch, nnkElse:
|
||||
let ofBranch = newNimNode(typeNode.kind)
|
||||
for i in 0 ..< typeNode.len-1:
|
||||
ofBranch.add copyNimTree(typeNode[i])
|
||||
let dstInner = newNimNode(nnkStmtListExpr)
|
||||
foldObjectBody(dstInner, typeNode[^1], tmpSym, jsonNode, jsonPath, depth + 1)
|
||||
# resOuter now contains the inner stmtList
|
||||
ofBranch.add dstInner
|
||||
dst[^1].expectKind nnkCaseStmt
|
||||
dst[^1].add ofBranch
|
||||
|
||||
of nnkObjectTy:
|
||||
typeNode[0].expectKind nnkEmpty
|
||||
typeNode[1].expectKind {nnkEmpty, nnkOfInherit}
|
||||
if typeNode[1].kind == nnkOfInherit:
|
||||
let base = typeNode[1][0]
|
||||
var impl = getTypeImpl(base)
|
||||
while impl.kind in {nnkRefTy, nnkPtrTy}:
|
||||
impl = getTypeImpl(impl[0])
|
||||
foldObjectBody(dst, impl, tmpSym, jsonNode, jsonPath, depth + 1)
|
||||
let body = typeNode[2]
|
||||
foldObjectBody(dst, body, tmpSym, jsonNode, jsonPath, depth + 1)
|
||||
|
||||
else:
|
||||
result = quote do:
|
||||
verifyJsonKind(`jsonNode`, {JObject}, astToStr(`jsonNode`));
|
||||
`result`
|
||||
error("unhandled kind: " & $typeNode.kind, typeNode)
|
||||
|
||||
of nnkEnumTy:
|
||||
let instType = toIdentNode(getTypeInst(typeName))
|
||||
let getEnumCall = createGetEnumCall(jsonNode, instType)
|
||||
result = quote do:
|
||||
(
|
||||
`getEnumCall`
|
||||
)
|
||||
of nnkSym:
|
||||
let name = normalize($typeName.getTypeImpl())
|
||||
case name
|
||||
of "string":
|
||||
result = quote do:
|
||||
(
|
||||
verifyJsonKind(`jsonNode`, {JString, JNull}, astToStr(`jsonNode`));
|
||||
if `jsonNode`.kind == JNull: "" else: `jsonNode`.str
|
||||
)
|
||||
of "biggestint":
|
||||
result = quote do:
|
||||
(
|
||||
verifyJsonKind(`jsonNode`, {JInt}, astToStr(`jsonNode`));
|
||||
`jsonNode`.num
|
||||
)
|
||||
of "bool":
|
||||
result = quote do:
|
||||
(
|
||||
verifyJsonKind(`jsonNode`, {JBool}, astToStr(`jsonNode`));
|
||||
`jsonNode`.bval
|
||||
)
|
||||
|
||||
macro assignObjectImpl[T](dst: var T; jsonNode: JsonNode; jsonPath: string) =
|
||||
let typeSym = getTypeInst(dst)
|
||||
result = newStmtList()
|
||||
if typeSym.kind in {nnkTupleTy, nnkTupleConstr}:
|
||||
# both, `dst` and `typeSym` don't have good lineinfo. But nothing
|
||||
# else is available here.
|
||||
detectIncompatibleType(typeSym, dst)
|
||||
foldObjectBody(result, typeSym, dst, jsonNode, jsonPath, 0)
|
||||
else:
|
||||
if name.startsWith("int") or name.startsWith("uint"):
|
||||
result = quote do:
|
||||
(
|
||||
verifyJsonKind(`jsonNode`, {JInt}, astToStr(`jsonNode`));
|
||||
`jsonNode`.num.`obj`
|
||||
)
|
||||
elif name.startsWith("float"):
|
||||
result = quote do:
|
||||
(
|
||||
verifyJsonKind(`jsonNode`, {JInt, JFloat}, astToStr(`jsonNode`));
|
||||
if `jsonNode`.kind == JFloat: `jsonNode`.fnum.`obj` else: `jsonNode`.num.`obj`
|
||||
)
|
||||
else:
|
||||
doAssert false, "Unable to process nnkSym " & $typeName
|
||||
else:
|
||||
doAssert false, "Unable to process type: " & $obj.kind
|
||||
foldObjectBody(result, typeSym.getTypeImpl, dst, jsonNode, jsonPath, 0)
|
||||
|
||||
doAssert(not result.isNil(), "processType not initialised.")
|
||||
proc initFromJson[T : object|tuple](dst: var T; jsonNode: JsonNode; jsonPath: string) =
|
||||
assignObjectImpl(dst, jsonNode, jsonPath)
|
||||
|
||||
import options
|
||||
proc workaroundMacroNone[T](): Option[T] =
|
||||
none(T)
|
||||
proc to*[T](node: JsonNode, t: typedesc[T]): T =
|
||||
## `Unmarshals`:idx: the specified node into the object type specified.
|
||||
##
|
||||
## Known limitations:
|
||||
##
|
||||
## * Heterogeneous arrays are not supported.
|
||||
## * Sets in object variants are not supported.
|
||||
## * Not nil annotations are not supported.
|
||||
##
|
||||
## Example:
|
||||
##
|
||||
## .. code-block:: Nim
|
||||
## let jsonNode = parseJson("""
|
||||
## {
|
||||
## "person": {
|
||||
## "name": "Nimmer",
|
||||
## "age": 21
|
||||
## },
|
||||
## "list": [1, 2, 3, 4]
|
||||
## }
|
||||
## """)
|
||||
##
|
||||
## type
|
||||
## Person = object
|
||||
## name: string
|
||||
## age: int
|
||||
##
|
||||
## Data = object
|
||||
## person: Person
|
||||
## list: seq[int]
|
||||
##
|
||||
## var data = to(jsonNode, Data)
|
||||
## doAssert data.person.name == "Nimmer"
|
||||
## doAssert data.person.age == 21
|
||||
## doAssert data.list == @[1, 2, 3, 4]
|
||||
|
||||
proc depth(n: NimNode, current = 0): int =
|
||||
result = 1
|
||||
for child in n:
|
||||
let d = 1 + child.depth(current + 1)
|
||||
if d > result:
|
||||
result = d
|
||||
|
||||
proc createConstructor(typeSym, jsonNode: NimNode): NimNode =
|
||||
## Accepts a type description, i.e. "ref Type", "seq[Type]", "Type" etc.
|
||||
##
|
||||
## The ``jsonNode`` refers to the node variable that we are deserialising.
|
||||
##
|
||||
## Returns an object constructor node.
|
||||
# echo("--createConsuctor-- \n", treeRepr(typeSym))
|
||||
# echo()
|
||||
|
||||
if depth(jsonNode) > 150:
|
||||
error("The `to` macro does not support ref objects with cycles.", jsonNode)
|
||||
|
||||
case typeSym.kind
|
||||
of nnkBracketExpr:
|
||||
var bracketName = ($typeSym[0]).normalize
|
||||
case bracketName
|
||||
of "option":
|
||||
# TODO: Would be good to verify that this is Option[T] from
|
||||
# options module I suppose.
|
||||
let lenientJsonNode = transformJsonIndexer(jsonNode)
|
||||
|
||||
let optionGeneric = typeSym[1]
|
||||
let value = createConstructor(typeSym[1], jsonNode)
|
||||
let workaround = bindSym("workaroundMacroNone") # TODO: Nim Bug: This shouldn't be necessary.
|
||||
|
||||
result = quote do:
|
||||
(
|
||||
if `lenientJsonNode`.isNil or `jsonNode`.kind == JNull: `workaround`[`optionGeneric`]() else: some[`optionGeneric`](`value`)
|
||||
)
|
||||
of "table", "orderedtable":
|
||||
let tableKeyType = typeSym[1]
|
||||
if ($tableKeyType).cmpIgnoreStyle("string") != 0:
|
||||
error("JSON doesn't support keys of type " & $tableKeyType)
|
||||
let tableValueType = typeSym[2]
|
||||
|
||||
let forLoopKey = genSym(nskForVar, "key")
|
||||
let indexerNode = createJsonIndexer(jsonNode, forLoopKey)
|
||||
let constructorNode = createConstructor(tableValueType, indexerNode)
|
||||
|
||||
let tableInit =
|
||||
if bracketName == "table":
|
||||
bindSym("initTable")
|
||||
else:
|
||||
bindSym("initOrderedTable")
|
||||
|
||||
# Create a statement expression containing a for loop.
|
||||
result = quote do:
|
||||
(
|
||||
var map = `tableInit`[`tableKeyType`, `tableValueType`]();
|
||||
verifyJsonKind(`jsonNode`, {JObject}, astToStr(`jsonNode`));
|
||||
for `forLoopKey` in keys(`jsonNode`.fields): map[
|
||||
`forLoopKey`] = `constructorNode`;
|
||||
map
|
||||
)
|
||||
of "ref":
|
||||
# Ref type.
|
||||
var typeName = $typeSym[1]
|
||||
# Remove the `:ObjectType` suffix.
|
||||
if typeName.endsWith(":ObjectType"):
|
||||
typeName = typeName[0 .. ^12]
|
||||
|
||||
let obj = getType(typeSym[1])
|
||||
result = processType(newIdentNode(typeName), obj, jsonNode, true)
|
||||
of "range":
|
||||
let typeNode = typeSym
|
||||
# Deduce the base type from one of the endpoints
|
||||
let baseType = getType(typeNode[1])
|
||||
|
||||
result = createConstructor(baseType, jsonNode)
|
||||
of "seq":
|
||||
let seqT = typeSym[1]
|
||||
let forLoopI = genSym(nskForVar, "i")
|
||||
let indexerNode = createJsonIndexer(jsonNode, forLoopI)
|
||||
let constructorNode = createConstructor(detectDistinctType(seqT), indexerNode)
|
||||
|
||||
# Create a statement expression containing a for loop.
|
||||
result = quote do:
|
||||
(
|
||||
var list: `typeSym` = @[];
|
||||
verifyJsonKind(`jsonNode`, {JArray}, astToStr(`jsonNode`));
|
||||
for `forLoopI` in 0 ..< `jsonNode`.len: list.add(`constructorNode`);
|
||||
list
|
||||
)
|
||||
of "array":
|
||||
let arrayT = typeSym[2]
|
||||
let forLoopI = genSym(nskForVar, "i")
|
||||
let indexerNode = createJsonIndexer(jsonNode, forLoopI)
|
||||
let constructorNode = createConstructor(arrayT, indexerNode)
|
||||
|
||||
# Create a statement expression containing a for loop.
|
||||
result = quote do:
|
||||
(
|
||||
var list: `typeSym`;
|
||||
verifyJsonKind(`jsonNode`, {JArray}, astToStr(`jsonNode`));
|
||||
for `forLoopI` in 0 ..< `jsonNode`.len: list[
|
||||
`forLoopI`] = `constructorNode`;
|
||||
list
|
||||
)
|
||||
of "tuple":
|
||||
let typeNode = getTypeImpl(typeSym)
|
||||
result = createConstructor(typeNode, jsonNode)
|
||||
else:
|
||||
# Generic type or some `seq[T]` alias
|
||||
let obj = getType(typeSym)
|
||||
case obj.kind
|
||||
of nnkBracketExpr:
|
||||
# probably a `seq[T]` alias
|
||||
let typeNode = getTypeImpl(typeSym)
|
||||
result = createConstructor(typeNode, jsonNode)
|
||||
else:
|
||||
# generic type
|
||||
result = processType(typeSym, obj, jsonNode, false)
|
||||
of nnkSym:
|
||||
# Handle JsonNode.
|
||||
if ($typeSym).cmpIgnoreStyle("jsonnode") == 0:
|
||||
return jsonNode
|
||||
|
||||
# Handle all other types.
|
||||
let obj = getType(typeSym)
|
||||
let typeNode = getTypeImpl(typeSym)
|
||||
if typeNode.typeKind == ntyDistinct:
|
||||
result = createConstructor(typeNode, jsonNode)
|
||||
elif obj.kind == nnkBracketExpr:
|
||||
# When `Sym "Foo"` turns out to be a `ref object` or `tuple`
|
||||
result = createConstructor(obj, jsonNode)
|
||||
else:
|
||||
result = processType(typeSym, obj, jsonNode, false)
|
||||
of nnkTupleTy:
|
||||
result = processType(typeSym, typeSym, jsonNode, false)
|
||||
of nnkPar, nnkTupleConstr:
|
||||
# TODO: The fact that `jsonNode` here works to give a good line number
|
||||
# is weird. Specifying typeSym should work but doesn't.
|
||||
error("Use a named tuple instead of: " & $toStrLit(typeSym), jsonNode)
|
||||
of nnkDistinctTy:
|
||||
var baseType = typeSym
|
||||
# solve nested distinct types
|
||||
while baseType.typeKind == ntyDistinct:
|
||||
let impl = getTypeImpl(baseType[0])
|
||||
if impl.typeKind != ntyDistinct:
|
||||
baseType = baseType[0]
|
||||
break
|
||||
baseType = impl
|
||||
let ret = createConstructor(baseType, jsonNode)
|
||||
let typeInst = getTypeInst(typeSym)
|
||||
result = quote do:
|
||||
(
|
||||
`typeInst`(`ret`)
|
||||
)
|
||||
else:
|
||||
doAssert false, "Unable to create constructor for: " & $typeSym.kind
|
||||
|
||||
doAssert(not result.isNil(), "Constructor not initialised.")
|
||||
|
||||
proc postProcess(node: NimNode): NimNode
|
||||
proc postProcessValue(value: NimNode): NimNode =
|
||||
## Looks for object constructors and calls the ``postProcess`` procedure
|
||||
## on them. Otherwise it just returns the node as-is.
|
||||
case value.kind
|
||||
of nnkObjConstr:
|
||||
result = postProcess(value)
|
||||
else:
|
||||
result = value
|
||||
for i in 0 ..< len(result):
|
||||
result[i] = postProcessValue(result[i])
|
||||
|
||||
proc postProcessExprColonExpr(exprColonExpr, resIdent: NimNode): NimNode =
|
||||
## Transform each field mapping in the ExprColonExpr into a simple
|
||||
## field assignment. Special processing is performed if the field mapping
|
||||
## has an if statement.
|
||||
##
|
||||
## ..code-block::plain
|
||||
## field: (if true: 12) -> if true: `resIdent`.field = 12
|
||||
expectKind(exprColonExpr, nnkExprColonExpr)
|
||||
let fieldName = exprColonExpr[0]
|
||||
let fieldValue = exprColonExpr[1]
|
||||
case fieldValue.kind
|
||||
of nnkIfStmt:
|
||||
doAssert fieldValue.len == 1, "Cannot postProcess two ElifBranches."
|
||||
expectKind(fieldValue[0], nnkElifBranch)
|
||||
|
||||
let cond = fieldValue[0][0]
|
||||
let bodyValue = postProcessValue(fieldValue[0][1])
|
||||
doAssert(bodyValue.kind != nnkNilLit)
|
||||
result =
|
||||
quote do:
|
||||
if `cond`:
|
||||
`resIdent`.`fieldName` = `bodyValue`
|
||||
else:
|
||||
let fieldValue = postProcessValue(fieldValue)
|
||||
doAssert(fieldValue.kind != nnkNilLit)
|
||||
result =
|
||||
quote do:
|
||||
`resIdent`.`fieldName` = `fieldValue`
|
||||
|
||||
|
||||
proc postProcess(node: NimNode): NimNode =
|
||||
## The ``createConstructor`` proc creates a ObjConstr node which contains
|
||||
## if statements for fields that may not be assignable (due to an object
|
||||
## variant). Nim doesn't handle this, but may do in the future.
|
||||
##
|
||||
## For simplicity, we post process the object constructor into multiple
|
||||
## assignments.
|
||||
##
|
||||
## For example:
|
||||
##
|
||||
## ..code-block::plain
|
||||
## Object( (var res = Object();
|
||||
## field: if true: 12 -> if true: res.field = 12;
|
||||
## ) res)
|
||||
result = newNimNode(nnkStmtListExpr)
|
||||
|
||||
expectKind(node, nnkObjConstr)
|
||||
|
||||
# Create the type.
|
||||
# -> var res = Object()
|
||||
var resIdent = genSym(nskVar, "res")
|
||||
var resType = node[0]
|
||||
|
||||
var objConstr = newTree(nnkObjConstr, resType)
|
||||
result.add newVarStmt(resIdent, objConstr)
|
||||
|
||||
# Process each ExprColonExpr.
|
||||
for i in 1..<len(node):
|
||||
if node[i].kind == nnkExprEqExpr:
|
||||
objConstr.add newTree(nnkExprColonExpr, node[i][0], node[i][1])
|
||||
else:
|
||||
result.add postProcessExprColonExpr(node[i], resIdent)
|
||||
|
||||
# Return the `res` variable.
|
||||
result.add(resIdent)
|
||||
|
||||
|
||||
macro to*(node: JsonNode, T: typedesc): untyped =
|
||||
## `Unmarshals`:idx: the specified node into the object type specified.
|
||||
##
|
||||
## Known limitations:
|
||||
##
|
||||
## * Heterogeneous arrays are not supported.
|
||||
## * Sets in object variants are not supported.
|
||||
## * Not nil annotations are not supported.
|
||||
##
|
||||
## Example:
|
||||
##
|
||||
## .. code-block:: Nim
|
||||
## let jsonNode = parseJson("""
|
||||
## {
|
||||
## "person": {
|
||||
## "name": "Nimmer",
|
||||
## "age": 21
|
||||
## },
|
||||
## "list": [1, 2, 3, 4]
|
||||
## }
|
||||
## """)
|
||||
##
|
||||
## type
|
||||
## Person = object
|
||||
## name: string
|
||||
## age: int
|
||||
##
|
||||
## Data = object
|
||||
## person: Person
|
||||
## list: seq[int]
|
||||
##
|
||||
## var data = to(jsonNode, Data)
|
||||
## doAssert data.person.name == "Nimmer"
|
||||
## doAssert data.person.age == 21
|
||||
## doAssert data.list == @[1, 2, 3, 4]
|
||||
|
||||
let typeNode = getTypeImpl(T)
|
||||
expectKind(typeNode, nnkBracketExpr)
|
||||
doAssert(($typeNode[0]).normalize == "typedesc")
|
||||
|
||||
# Create `temp` variable to store the result in case the user calls this
|
||||
# on `parseJson` (see bug #6604).
|
||||
result = newNimNode(nnkStmtListExpr)
|
||||
let temp = genSym(nskLet, "temp")
|
||||
result.add quote do:
|
||||
let `temp` = `node`
|
||||
|
||||
let constructor = createConstructor(typeNode[1], temp)
|
||||
result.add(postProcessValue(constructor))
|
||||
|
||||
# echo(treeRepr(result))
|
||||
# echo(toStrLit(result))
|
||||
initFromJson(result, node, "")
|
||||
|
||||
when false:
|
||||
import os
|
||||
|
||||
@@ -1143,6 +1143,15 @@ proc recv*(socket: Socket, data: var string, size: int, timeout = -1,
|
||||
flags = {SocketFlag.SafeDisconn}): int =
|
||||
## Higher-level version of ``recv``.
|
||||
##
|
||||
## Reads **up to** ``size`` bytes from ``socket`` into ``buf``.
|
||||
##
|
||||
## For buffered sockets this function will attempt to read all the requested
|
||||
## data. It will read this data in ``BufferSize`` chunks.
|
||||
##
|
||||
## For unbuffered sockets this function makes no effort to read
|
||||
## all the data requested. It will return as much data as the operating system
|
||||
## gives it.
|
||||
##
|
||||
## When 0 is returned the socket's connection has been closed.
|
||||
##
|
||||
## This function will throw an OSError exception when an error occurs. A value
|
||||
@@ -1171,6 +1180,15 @@ proc recv*(socket: Socket, size: int, timeout = -1,
|
||||
flags = {SocketFlag.SafeDisconn}): string {.inline.} =
|
||||
## Higher-level version of ``recv`` which returns a string.
|
||||
##
|
||||
## Reads **up to** ``size`` bytes from ``socket`` into ``buf``.
|
||||
##
|
||||
## For buffered sockets this function will attempt to read all the requested
|
||||
## data. It will read this data in ``BufferSize`` chunks.
|
||||
##
|
||||
## For unbuffered sockets this function makes no effort to read
|
||||
## all the data requested. It will return as much data as the operating system
|
||||
## gives it.
|
||||
##
|
||||
## When ``""`` is returned the socket's connection has been closed.
|
||||
##
|
||||
## This function will throw an OSError exception when an error occurs.
|
||||
|
||||
171
lib/pure/os.nim
171
lib/pure/os.nim
@@ -15,18 +15,18 @@
|
||||
## import os
|
||||
##
|
||||
## let myFile = "/path/to/my/file.nim"
|
||||
##
|
||||
##
|
||||
## let pathSplit = splitPath(myFile)
|
||||
## assert pathSplit.head == "/path/to/my"
|
||||
## assert pathSplit.tail == "file.nim"
|
||||
##
|
||||
##
|
||||
## assert parentDir(myFile) == "/path/to/my"
|
||||
##
|
||||
##
|
||||
## let fileSplit = splitFile(myFile)
|
||||
## assert fileSplit.dir == "/path/to/my"
|
||||
## assert fileSplit.name == "file"
|
||||
## assert fileSplit.ext == ".nim"
|
||||
##
|
||||
##
|
||||
## assert myFile.changeFileExt("c") == "/path/to/my/file.c"
|
||||
|
||||
##
|
||||
@@ -232,11 +232,92 @@ proc splitPath*(path: string): tuple[head, tail: string] {.
|
||||
result.head = ""
|
||||
result.tail = path
|
||||
|
||||
proc isAbsolute*(path: string): bool {.rtl, noSideEffect, extern: "nos$1", raises: [].} =
|
||||
## Checks whether a given `path` is absolute.
|
||||
##
|
||||
## On Windows, network paths are considered absolute too.
|
||||
runnableExamples:
|
||||
assert not "".isAbsolute
|
||||
assert not ".".isAbsolute
|
||||
when defined(posix):
|
||||
assert "/".isAbsolute
|
||||
assert not "a/".isAbsolute
|
||||
assert "/a/".isAbsolute
|
||||
|
||||
if len(path) == 0: return false
|
||||
|
||||
when doslikeFileSystem:
|
||||
var len = len(path)
|
||||
result = (path[0] in {'/', '\\'}) or
|
||||
(len > 1 and path[0] in {'a'..'z', 'A'..'Z'} and path[1] == ':')
|
||||
elif defined(macos):
|
||||
# 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):
|
||||
result = path[0] == '/'
|
||||
|
||||
when FileSystemCaseSensitive:
|
||||
template `!=?`(a, b: char): bool = a != b
|
||||
else:
|
||||
template `!=?`(a, b: char): bool = toLowerAscii(a) != toLowerAscii(b)
|
||||
|
||||
when doslikeFileSystem:
|
||||
proc isAbsFromCurrentDrive(path: string): bool {.noSideEffect, raises: []} =
|
||||
## An absolute path from the root of the current drive (e.g. "\foo")
|
||||
path.len > 0 and
|
||||
(path[0] == AltSep or
|
||||
(path[0] == DirSep and
|
||||
(path.len == 1 or path[1] notin {DirSep, AltSep, ':'})))
|
||||
|
||||
proc isUNCPrefix(path: string): bool {.noSideEffect, raises: []} =
|
||||
path[0] == DirSep and path[1] == DirSep
|
||||
|
||||
proc sameRoot(path1, path2: string): bool {.noSideEffect, raises: []} =
|
||||
## Return true if path1 and path2 have a same root.
|
||||
##
|
||||
## Detail of windows path formats:
|
||||
## https://docs.microsoft.com/en-us/dotnet/standard/io/file-path-formats
|
||||
|
||||
assert(isAbsolute(path1))
|
||||
assert(isAbsolute(path2))
|
||||
|
||||
let
|
||||
len1 = path1.len
|
||||
len2 = path2.len
|
||||
assert(len1 != 0 and len2 != 0)
|
||||
|
||||
if isAbsFromCurrentDrive(path1) and isAbsFromCurrentDrive(path2):
|
||||
return true
|
||||
elif len1 == 1 or len2 == 1:
|
||||
return false
|
||||
else:
|
||||
if path1[1] == ':' and path2[1] == ':':
|
||||
return path1[0].toLowerAscii() == path2[0].toLowerAscii()
|
||||
else:
|
||||
var
|
||||
p1, p2: PathIter
|
||||
pp1 = next(p1, path1)
|
||||
pp2 = next(p2, path2)
|
||||
if pp1[1] - pp1[0] == 1 and pp2[1] - pp2[0] == 1 and
|
||||
isUNCPrefix(path1) and isUNCPrefix(path2):
|
||||
#UNC
|
||||
var h = 0
|
||||
while p1.hasNext(path1) and p2.hasNext(path2) and h < 2:
|
||||
pp1 = next(p1, path1)
|
||||
pp2 = next(p2, path2)
|
||||
let diff = pp1[1] - pp1[0]
|
||||
if diff != pp2[1] - pp2[0]:
|
||||
return false
|
||||
for i in 0..diff:
|
||||
if path1[i + pp1[0]] !=? path2[i + pp2[0]]:
|
||||
return false
|
||||
inc h
|
||||
return h == 2
|
||||
else:
|
||||
return false
|
||||
|
||||
proc relativePath*(path, base: string; sep = DirSep): string {.
|
||||
noSideEffect, rtl, extern: "nos$1", raises: [].} =
|
||||
## Converts `path` to a path relative to `base`.
|
||||
@@ -245,6 +326,10 @@ proc relativePath*(path, base: string; sep = DirSep): string {.
|
||||
## this can be useful to ensure the relative path only contains `'/'`
|
||||
## so that it can be used for URL constructions.
|
||||
##
|
||||
## On windows, if a root of `path` and a root of `base` are different,
|
||||
## returns `path` as is because it is impossible to make a relative path.
|
||||
## That means an absolute path can be returned.
|
||||
##
|
||||
## See also:
|
||||
## * `splitPath proc <#splitPath,string>`_
|
||||
## * `parentDir proc <#parentDir,string>`_
|
||||
@@ -256,9 +341,13 @@ proc relativePath*(path, base: string; sep = DirSep): string {.
|
||||
assert relativePath("/Users/me/bar/z.nim", "/Users/me", '/') == "bar/z.nim"
|
||||
assert relativePath("", "/users/moo", '/') == ""
|
||||
|
||||
# Todo: If on Windows, path and base do not agree on the drive letter,
|
||||
# return `path` as is.
|
||||
if path.len == 0: return ""
|
||||
|
||||
when doslikeFileSystem:
|
||||
if isAbsolute(path) and isAbsolute(base):
|
||||
if not sameRoot(path, base):
|
||||
return path
|
||||
|
||||
var f, b: PathIter
|
||||
var ff = (0, -1)
|
||||
var bb = (0, -1) # (int, int)
|
||||
@@ -645,32 +734,6 @@ proc cmpPaths*(pathA, pathB: string): int {.
|
||||
else:
|
||||
result = cmpIgnoreCase(a, b)
|
||||
|
||||
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:
|
||||
assert not "".isAbsolute
|
||||
assert not ".".isAbsolute
|
||||
when defined(posix):
|
||||
assert "/".isAbsolute
|
||||
assert not "a/".isAbsolute
|
||||
assert "/a/".isAbsolute
|
||||
|
||||
if len(path) == 0: return false
|
||||
|
||||
when doslikeFileSystem:
|
||||
var len = len(path)
|
||||
result = (path[0] in {'/', '\\'}) or
|
||||
(len > 1 and path[0] in {'a'..'z', 'A'..'Z'} and path[1] == ':')
|
||||
elif defined(macos):
|
||||
# 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):
|
||||
result = path[0] == '/'
|
||||
|
||||
proc unixToNativePath*(path: string, drive=""): string {.
|
||||
noSideEffect, rtl, extern: "nos$1".} =
|
||||
## Converts an UNIX-like path to a native one.
|
||||
@@ -2658,6 +2721,47 @@ when not weirdTarget and (defined(linux) or defined(solaris) or defined(bsd) or
|
||||
len = readlink(procPath, result, len)
|
||||
setLen(result, len)
|
||||
|
||||
when defined(openbsd):
|
||||
proc isExecutable(path: string): bool =
|
||||
let p = getFilePermissions(path)
|
||||
result = fpUserExec in p and fpGroupExec in p and fpOthersExec in p
|
||||
|
||||
proc getApplOpenBsd(): string =
|
||||
# similar to getApplHeuristic, but checks current working directory
|
||||
when declared(paramStr):
|
||||
result = ""
|
||||
|
||||
# POSIX guaranties that this contains the executable
|
||||
# as it has been executed by the calling process
|
||||
let exePath = string(paramStr(0))
|
||||
|
||||
if len(exePath) == 0:
|
||||
return ""
|
||||
|
||||
if exePath[0] == DirSep:
|
||||
# path is absolute
|
||||
result = exePath
|
||||
else:
|
||||
# not an absolute path, check if it's relative to the current working directory
|
||||
for i in 1..<len(exePath):
|
||||
if exePath[i] == DirSep:
|
||||
result = joinPath(getCurrentDir(), exePath)
|
||||
break
|
||||
|
||||
if len(result) > 0:
|
||||
if isExecutable(result):
|
||||
return expandFilename(result)
|
||||
|
||||
return ""
|
||||
|
||||
# search in path
|
||||
for p in split(string(getEnv("PATH")), {PathSep}):
|
||||
var x = joinPath(p, exePath)
|
||||
if existsFile(x) and isExecutable(x):
|
||||
return expandFilename(x)
|
||||
else:
|
||||
result = ""
|
||||
|
||||
when not (defined(windows) or defined(macosx) or weirdTarget):
|
||||
proc getApplHeuristic(): string =
|
||||
when declared(paramStr):
|
||||
@@ -2761,6 +2865,9 @@ proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect], noN
|
||||
result = getApplFreebsd()
|
||||
elif defined(haiku):
|
||||
result = getApplHaiku()
|
||||
elif defined(openbsd):
|
||||
result = getApplOpenBsd()
|
||||
|
||||
# little heuristic that may work on other POSIX-like systems:
|
||||
if result.len == 0:
|
||||
result = getApplHeuristic()
|
||||
|
||||
@@ -561,7 +561,7 @@ proc writeConfig*(dict: Config, filename: string) =
|
||||
dict.writeConfig(fileStream)
|
||||
|
||||
proc getSectionValue*(dict: Config, section, key: string): string =
|
||||
## Gets the Key value of the specified Section.
|
||||
## Gets the Key value of the specified Section, returns an empty string if the key does not exist.
|
||||
if dict.hasKey(section):
|
||||
if dict[section].hasKey(key):
|
||||
result = dict[section][key]
|
||||
|
||||
@@ -84,11 +84,11 @@ include "system/inclrtl"
|
||||
{.push debugger: off.}
|
||||
|
||||
when defined(JS):
|
||||
type ui = uint32
|
||||
type Ui = uint32
|
||||
|
||||
const randMax = 4_294_967_295u32
|
||||
else:
|
||||
type ui = uint64
|
||||
type Ui = uint64
|
||||
|
||||
const randMax = 18_446_744_073_709_551_615u64
|
||||
|
||||
@@ -106,7 +106,7 @@ type
|
||||
## Many procs have two variations: one that takes in a Rand parameter and
|
||||
## another that uses the default generator. The procs that use the default
|
||||
## generator are **not** thread-safe!
|
||||
a0, a1: ui
|
||||
a0, a1: Ui
|
||||
|
||||
when defined(JS):
|
||||
var state = Rand(
|
||||
@@ -118,8 +118,8 @@ else:
|
||||
a0: 0x69B4C98CB8530805u64,
|
||||
a1: 0xFED1DD3004688D67CAu64) # global for backwards compatibility
|
||||
|
||||
proc rotl(x, k: ui): ui =
|
||||
result = (x shl k) or (x shr (ui(64) - k))
|
||||
proc rotl(x, k: Ui): Ui =
|
||||
result = (x shl k) or (x shr (Ui(64) - k))
|
||||
|
||||
proc next*(r: var Rand): uint64 =
|
||||
## Computes a random ``uint64`` number using the given state.
|
||||
@@ -195,11 +195,11 @@ proc skipRandomNumbers*(s: var Rand) =
|
||||
else:
|
||||
const helper = [0xbeac0467eba5facbu64, 0xd86b048b86aa9922u64]
|
||||
var
|
||||
s0 = ui 0
|
||||
s1 = ui 0
|
||||
s0 = Ui 0
|
||||
s1 = Ui 0
|
||||
for i in 0..high(helper):
|
||||
for b in 0 ..< 64:
|
||||
if (helper[i] and (ui(1) shl ui(b))) != 0:
|
||||
if (helper[i] and (Ui(1) shl Ui(b))) != 0:
|
||||
s0 = s0 xor s.a0
|
||||
s1 = s1 xor s.a1
|
||||
discard next(s)
|
||||
@@ -210,7 +210,7 @@ proc random*(max: int): int {.benign, deprecated:
|
||||
"Deprecated since v0.18.0; use 'rand' instead".} =
|
||||
while true:
|
||||
let x = next(state)
|
||||
if x < randMax - (randMax mod ui(max)):
|
||||
if x < randMax - (randMax mod Ui(max)):
|
||||
return int(x mod uint64(max))
|
||||
|
||||
proc random*(max: float): float {.benign, deprecated:
|
||||
@@ -247,7 +247,7 @@ proc rand*(r: var Rand; max: Natural): int {.benign.} =
|
||||
if max == 0: return
|
||||
while true:
|
||||
let x = next(r)
|
||||
if x <= randMax - (randMax mod ui(max)):
|
||||
if x <= randMax - (randMax mod Ui(max)):
|
||||
return int(x mod (uint64(max)+1u64))
|
||||
|
||||
proc rand*(max: int): int {.benign.} =
|
||||
@@ -570,8 +570,8 @@ proc initRand*(seed: int64): Rand =
|
||||
let now = getTime()
|
||||
var r2 = initRand(now.toUnix * 1_000_000_000 + now.nanosecond)
|
||||
doAssert seed != 0 # 0 causes `rand(int)` to always return 0 for example.
|
||||
result.a0 = ui(seed shr 16)
|
||||
result.a1 = ui(seed and 0xffff)
|
||||
result.a0 = Ui(seed shr 16)
|
||||
result.a1 = Ui(seed and 0xffff)
|
||||
discard next(result)
|
||||
|
||||
proc randomize*(seed: int64) {.benign.} =
|
||||
@@ -642,7 +642,7 @@ when not defined(nimscript):
|
||||
## * `randomize proc<#randomize,int64>`_ that accepts a seed
|
||||
## * `initRand proc<#initRand,int64>`_
|
||||
when defined(JS):
|
||||
let time = int64(times.epochTime() * 1000)
|
||||
let time = int64(times.epochTime() * 1000) and 0x7fff_ffff
|
||||
randomize(time)
|
||||
else:
|
||||
let now = times.getTime()
|
||||
|
||||
@@ -76,7 +76,7 @@ One very nice advantage over regular expressions is that ``scanf`` is
|
||||
extensible with ordinary Nim procs. The proc is either enclosed in ``${}``
|
||||
or in ``$[]``. ``${}`` matches and binds the result
|
||||
to a variable (that was passed to the ``scanf`` macro) while ``$[]`` merely
|
||||
optional tokens.
|
||||
matches optional tokens without any result binding.
|
||||
|
||||
|
||||
In this example, we define a helper proc ``someSep`` that skips some separators
|
||||
|
||||
@@ -180,7 +180,6 @@ macro `[]`*(lc: ListComprehension, comp, typ: untyped): untyped {.deprecated.} =
|
||||
newNimNode(nnkBracket))),
|
||||
result))))
|
||||
|
||||
|
||||
macro dump*(x: typed): untyped =
|
||||
## Dumps the content of an expression, useful for debugging.
|
||||
## It accepts any expression and prints a textual representation
|
||||
|
||||
@@ -186,7 +186,7 @@ proc delOutputFormatter*(formatter: OutputFormatter) =
|
||||
keepIf(formatters, proc (x: OutputFormatter): bool =
|
||||
x != formatter)
|
||||
|
||||
proc newConsoleOutputFormatter*(outputLevel: OutputLevel = PRINT_ALL,
|
||||
proc newConsoleOutputFormatter*(outputLevel: OutputLevel = OutputLevel.PRINT_ALL,
|
||||
colorOutput = true): <//>ConsoleOutputFormatter =
|
||||
ConsoleOutputFormatter(
|
||||
outputLevel: outputLevel,
|
||||
@@ -207,7 +207,7 @@ proc defaultConsoleFormatter*(): <//>ConsoleOutputFormatter =
|
||||
colorOutput = true
|
||||
elif existsEnv("NIMTEST_NO_COLOR"):
|
||||
colorOutput = false
|
||||
var outputLevel = PRINT_ALL
|
||||
var outputLevel = OutputLevel.PRINT_ALL
|
||||
if envOutLvl.len > 0:
|
||||
for opt in countup(low(OutputLevel), high(OutputLevel)):
|
||||
if $opt == envOutLvl:
|
||||
@@ -240,17 +240,17 @@ method failureOccurred*(formatter: ConsoleOutputFormatter,
|
||||
method testEnded*(formatter: ConsoleOutputFormatter, testResult: TestResult) =
|
||||
formatter.isInTest = false
|
||||
|
||||
if formatter.outputLevel != PRINT_NONE and
|
||||
(formatter.outputLevel == PRINT_ALL or testResult.status == FAILED):
|
||||
if formatter.outputLevel != OutputLevel.PRINT_NONE and
|
||||
(formatter.outputLevel == OutputLevel.PRINT_ALL or testResult.status == TestStatus.FAILED):
|
||||
let prefix = if testResult.suiteName.len > 0: " " else: ""
|
||||
template rawPrint() = echo(prefix, "[", $testResult.status, "] ",
|
||||
testResult.testName)
|
||||
when not defined(ECMAScript):
|
||||
if formatter.colorOutput and not defined(ECMAScript):
|
||||
var color = case testResult.status
|
||||
of OK: fgGreen
|
||||
of FAILED: fgRed
|
||||
of SKIPPED: fgYellow
|
||||
of TestStatus.OK: fgGreen
|
||||
of TestStatus.FAILED: fgRed
|
||||
of TestStatus.SKIPPED: fgYellow
|
||||
styledEcho styleBright, color, prefix, "[", $testResult.status, "] ",
|
||||
resetStyle, testResult.testName
|
||||
else:
|
||||
@@ -318,11 +318,11 @@ method testEnded*(formatter: JUnitOutputFormatter, testResult: TestResult) =
|
||||
formatter.stream.writeLine("\t\t<testcase name=\"$#\" time=\"$#\">" % [
|
||||
xmlEscape(testResult.testName), timeStr])
|
||||
case testResult.status
|
||||
of OK:
|
||||
of TestStatus.OK:
|
||||
discard
|
||||
of SKIPPED:
|
||||
of TestStatus.SKIPPED:
|
||||
formatter.stream.writeLine("<skipped />")
|
||||
of FAILED:
|
||||
of TestStatus.FAILED:
|
||||
let failureMsg = if formatter.testStackTrace.len > 0 and
|
||||
formatter.testErrors.len > 0:
|
||||
xmlEscape(formatter.testErrors[^1])
|
||||
@@ -498,7 +498,7 @@ template test*(name, body) {.dirty.} =
|
||||
|
||||
if shouldRun(when declared(testSuiteName): testSuiteName else: "", name):
|
||||
checkpoints = @[]
|
||||
var testStatusIMPL {.inject.} = OK
|
||||
var testStatusIMPL {.inject.} = TestStatus.OK
|
||||
|
||||
for formatter in formatters:
|
||||
formatter.testStarted(name)
|
||||
@@ -518,7 +518,7 @@ template test*(name, body) {.dirty.} =
|
||||
fail()
|
||||
|
||||
finally:
|
||||
if testStatusIMPL == FAILED:
|
||||
if testStatusIMPL == TestStatus.FAILED:
|
||||
programResult = 1
|
||||
let testResult = TestResult(
|
||||
suiteName: when declared(testSuiteName): testSuiteName else: "",
|
||||
@@ -558,7 +558,7 @@ template fail* =
|
||||
bind ensureInitialized
|
||||
|
||||
when declared(testStatusIMPL):
|
||||
testStatusIMPL = FAILED
|
||||
testStatusIMPL = TestStatus.FAILED
|
||||
else:
|
||||
programResult = 1
|
||||
|
||||
@@ -589,7 +589,7 @@ template skip* =
|
||||
## skip()
|
||||
bind checkpoints
|
||||
|
||||
testStatusIMPL = SKIPPED
|
||||
testStatusIMPL = TestStatus.SKIPPED
|
||||
checkpoints = @[]
|
||||
|
||||
macro check*(conditions: untyped): untyped =
|
||||
|
||||
207
lib/system.nim
207
lib/system.nim
@@ -671,7 +671,7 @@ include "system/inclrtl"
|
||||
const NoFakeVars* = defined(nimscript) ## `true` if the backend doesn't support \
|
||||
## "fake variables" like `var EBADF {.importc.}: cint`.
|
||||
|
||||
when not defined(JS) and not defined(gcDestructors):
|
||||
when not defined(JS) and not defined(nimSeqsV2):
|
||||
type
|
||||
TGenericSeq {.compilerproc, pure, inheritable.} = object
|
||||
len, reserved: int
|
||||
@@ -684,7 +684,7 @@ when not defined(JS) and not defined(gcDestructors):
|
||||
NimString = ptr NimStringDesc
|
||||
|
||||
when not defined(JS) and not defined(nimscript):
|
||||
when not defined(gcDestructors):
|
||||
when not defined(nimSeqsV2):
|
||||
template space(s: PGenericSeq): int {.dirty.} =
|
||||
s.reserved and not (seqShallowFlag or strlitFlag)
|
||||
when not defined(nimV2):
|
||||
@@ -1020,7 +1020,7 @@ when not defined(JS):
|
||||
## assert len(x) == 3
|
||||
## x[0] = 10
|
||||
result = newSeqOfCap[T](len)
|
||||
when defined(gcDestructors):
|
||||
when defined(nimSeqsV2):
|
||||
cast[ptr int](addr result)[] = len
|
||||
else:
|
||||
var s = cast[PGenericSeq](result)
|
||||
@@ -1323,65 +1323,51 @@ proc `mod`*(x, y: int16): int16 {.magic: "ModI", noSideEffect.}
|
||||
proc `mod`*(x, y: int32): int32 {.magic: "ModI", noSideEffect.}
|
||||
proc `mod`*(x, y: int64): int64 {.magic: "ModI", noSideEffect.}
|
||||
|
||||
when defined(nimNewShiftOps):
|
||||
|
||||
when defined(nimOldShiftRight) or not defined(nimAshr):
|
||||
const shrDepMessage = "`shr` will become sign preserving."
|
||||
proc `shr`*(x: int, y: SomeInteger): int {.magic: "ShrI", noSideEffect, deprecated: shrDepMessage.}
|
||||
proc `shr`*(x: int8, y: SomeInteger): int8 {.magic: "ShrI", noSideEffect, deprecated: shrDepMessage.}
|
||||
proc `shr`*(x: int16, y: SomeInteger): int16 {.magic: "ShrI", noSideEffect, deprecated: shrDepMessage.}
|
||||
proc `shr`*(x: int32, y: SomeInteger): int32 {.magic: "ShrI", noSideEffect, deprecated: shrDepMessage.}
|
||||
proc `shr`*(x: int64, y: SomeInteger): int64 {.magic: "ShrI", noSideEffect, deprecated: shrDepMessage.}
|
||||
else:
|
||||
proc `shr`*(x: int, y: SomeInteger): int {.magic: "AshrI", noSideEffect.}
|
||||
## Computes the `shift right` operation of `x` and `y`, filling
|
||||
## vacant bit positions with the sign bit.
|
||||
##
|
||||
## **Note**: `Operator precedence <manual.html#syntax-precedence>`_
|
||||
## is different than in *C*.
|
||||
##
|
||||
## See also:
|
||||
## * `ashr proc <#ashr,int,SomeInteger>`_ for arithmetic shift right
|
||||
##
|
||||
## .. code-block:: Nim
|
||||
## 0b0001_0000'i8 shr 2 == 0b0000_0100'i8
|
||||
## 0b0000_0001'i8 shr 1 == 0b0000_0000'i8
|
||||
## 0b1000_0000'i8 shr 4 == 0b1111_1000'i8
|
||||
## -1 shr 5 == -1
|
||||
## 1 shr 5 == 0
|
||||
## 16 shr 2 == 4
|
||||
## -16 shr 2 == -4
|
||||
proc `shr`*(x: int8, y: SomeInteger): int8 {.magic: "AshrI", noSideEffect.}
|
||||
proc `shr`*(x: int16, y: SomeInteger): int16 {.magic: "AshrI", noSideEffect.}
|
||||
proc `shr`*(x: int32, y: SomeInteger): int32 {.magic: "AshrI", noSideEffect.}
|
||||
proc `shr`*(x: int64, y: SomeInteger): int64 {.magic: "AshrI", noSideEffect.}
|
||||
|
||||
|
||||
proc `shl`*(x: int, y: SomeInteger): int {.magic: "ShlI", noSideEffect.}
|
||||
## Computes the `shift left` operation of `x` and `y`.
|
||||
when defined(nimOldShiftRight) or not defined(nimAshr):
|
||||
const shrDepMessage = "`shr` will become sign preserving."
|
||||
proc `shr`*(x: int, y: SomeInteger): int {.magic: "ShrI", noSideEffect, deprecated: shrDepMessage.}
|
||||
proc `shr`*(x: int8, y: SomeInteger): int8 {.magic: "ShrI", noSideEffect, deprecated: shrDepMessage.}
|
||||
proc `shr`*(x: int16, y: SomeInteger): int16 {.magic: "ShrI", noSideEffect, deprecated: shrDepMessage.}
|
||||
proc `shr`*(x: int32, y: SomeInteger): int32 {.magic: "ShrI", noSideEffect, deprecated: shrDepMessage.}
|
||||
proc `shr`*(x: int64, y: SomeInteger): int64 {.magic: "ShrI", noSideEffect, deprecated: shrDepMessage.}
|
||||
else:
|
||||
proc `shr`*(x: int, y: SomeInteger): int {.magic: "AshrI", noSideEffect.}
|
||||
## Computes the `shift right` operation of `x` and `y`, filling
|
||||
## vacant bit positions with the sign bit.
|
||||
##
|
||||
## **Note**: `Operator precedence <manual.html#syntax-precedence>`_
|
||||
## is different than in *C*.
|
||||
##
|
||||
## See also:
|
||||
## * `ashr proc <#ashr,int,SomeInteger>`_ for arithmetic shift right
|
||||
##
|
||||
## .. code-block:: Nim
|
||||
## 1'i32 shl 4 == 0x0000_0010
|
||||
## 1'i64 shl 4 == 0x0000_0000_0000_0010
|
||||
proc `shl`*(x: int8, y: SomeInteger): int8 {.magic: "ShlI", noSideEffect.}
|
||||
proc `shl`*(x: int16, y: SomeInteger): int16 {.magic: "ShlI", noSideEffect.}
|
||||
proc `shl`*(x: int32, y: SomeInteger): int32 {.magic: "ShlI", noSideEffect.}
|
||||
proc `shl`*(x: int64, y: SomeInteger): int64 {.magic: "ShlI", noSideEffect.}
|
||||
else:
|
||||
proc `shr`*(x, y: int): int {.magic: "ShrI", noSideEffect.}
|
||||
proc `shr`*(x, y: int8): int8 {.magic: "ShrI", noSideEffect.}
|
||||
proc `shr`*(x, y: int16): int16 {.magic: "ShrI", noSideEffect.}
|
||||
proc `shr`*(x, y: int32): int32 {.magic: "ShrI", noSideEffect.}
|
||||
proc `shr`*(x, y: int64): int64 {.magic: "ShrI", noSideEffect.}
|
||||
## 0b0001_0000'i8 shr 2 == 0b0000_0100'i8
|
||||
## 0b0000_0001'i8 shr 1 == 0b0000_0000'i8
|
||||
## 0b1000_0000'i8 shr 4 == 0b1111_1000'i8
|
||||
## -1 shr 5 == -1
|
||||
## 1 shr 5 == 0
|
||||
## 16 shr 2 == 4
|
||||
## -16 shr 2 == -4
|
||||
proc `shr`*(x: int8, y: SomeInteger): int8 {.magic: "AshrI", noSideEffect.}
|
||||
proc `shr`*(x: int16, y: SomeInteger): int16 {.magic: "AshrI", noSideEffect.}
|
||||
proc `shr`*(x: int32, y: SomeInteger): int32 {.magic: "AshrI", noSideEffect.}
|
||||
proc `shr`*(x: int64, y: SomeInteger): int64 {.magic: "AshrI", noSideEffect.}
|
||||
|
||||
proc `shl`*(x, y: int): int {.magic: "ShlI", noSideEffect.}
|
||||
proc `shl`*(x, y: int8): int8 {.magic: "ShlI", noSideEffect.}
|
||||
proc `shl`*(x, y: int16): int16 {.magic: "ShlI", noSideEffect.}
|
||||
proc `shl`*(x, y: int32): int32 {.magic: "ShlI", noSideEffect.}
|
||||
proc `shl`*(x, y: int64): int64 {.magic: "ShlI", noSideEffect.}
|
||||
|
||||
proc `shl`*(x: int, y: SomeInteger): int {.magic: "ShlI", noSideEffect.}
|
||||
## Computes the `shift left` operation of `x` and `y`.
|
||||
##
|
||||
## **Note**: `Operator precedence <manual.html#syntax-precedence>`_
|
||||
## is different than in *C*.
|
||||
##
|
||||
## .. code-block:: Nim
|
||||
## 1'i32 shl 4 == 0x0000_0010
|
||||
## 1'i64 shl 4 == 0x0000_0000_0000_0010
|
||||
proc `shl`*(x: int8, y: SomeInteger): int8 {.magic: "ShlI", noSideEffect.}
|
||||
proc `shl`*(x: int16, y: SomeInteger): int16 {.magic: "ShlI", noSideEffect.}
|
||||
proc `shl`*(x: int32, y: SomeInteger): int32 {.magic: "ShlI", noSideEffect.}
|
||||
proc `shl`*(x: int64, y: SomeInteger): int64 {.magic: "ShlI", noSideEffect.}
|
||||
|
||||
when defined(nimAshr):
|
||||
proc ashr*(x: int, y: SomeInteger): int {.magic: "AshrI", noSideEffect.}
|
||||
@@ -1518,54 +1504,105 @@ template `>%`*(x, y: untyped): untyped = y <% x
|
||||
|
||||
|
||||
# unsigned integer operations:
|
||||
proc `not`*[T: SomeUnsignedInt](x: T): T {.magic: "BitnotI", noSideEffect.}
|
||||
proc `not`*(x: uint): uint {.magic: "BitnotI", noSideEffect.}
|
||||
## Computes the `bitwise complement` of the integer `x`.
|
||||
proc `not`*(x: uint8): uint8 {.magic: "BitnotI", noSideEffect.}
|
||||
proc `not`*(x: uint16): uint16 {.magic: "BitnotI", noSideEffect.}
|
||||
proc `not`*(x: uint32): uint32 {.magic: "BitnotI", noSideEffect.}
|
||||
proc `not`*(x: uint64): uint64 {.magic: "BitnotI", noSideEffect.}
|
||||
|
||||
when defined(nimNewShiftOps):
|
||||
proc `shr`*[T: SomeUnsignedInt](x: T, y: SomeInteger): T {.magic: "ShrI", noSideEffect.}
|
||||
## Computes the `shift right` operation of `x` and `y`.
|
||||
proc `shl`*[T: SomeUnsignedInt](x: T, y: SomeInteger): T {.magic: "ShlI", noSideEffect.}
|
||||
## Computes the `shift left` operation of `x` and `y`.
|
||||
else:
|
||||
proc `shr`*[T: SomeUnsignedInt](x, y: T): T {.magic: "ShrI", noSideEffect.}
|
||||
## Computes the `shift right` operation of `x` and `y`.
|
||||
proc `shl`*[T: SomeUnsignedInt](x, y: T): T {.magic: "ShlI", noSideEffect.}
|
||||
## Computes the `shift left` operation of `x` and `y`.
|
||||
proc `shr`*(x: uint, y: SomeInteger): uint {.magic: "ShrI", noSideEffect.}
|
||||
## Computes the `shift right` operation of `x` and `y`.
|
||||
proc `shr`*(x: uint8, y: SomeInteger): uint8 {.magic: "ShrI", noSideEffect.}
|
||||
proc `shr`*(x: uint16, y: SomeInteger): uint16 {.magic: "ShrI", noSideEffect.}
|
||||
proc `shr`*(x: uint32, y: SomeInteger): uint32 {.magic: "ShrI", noSideEffect.}
|
||||
proc `shr`*(x: uint64, y: SomeInteger): uint64 {.magic: "ShrI", noSideEffect.}
|
||||
|
||||
proc `and`*[T: SomeUnsignedInt](x, y: T): T {.magic: "BitandI", noSideEffect.}
|
||||
proc `shl`*(x: uint, y: SomeInteger): uint {.magic: "ShlI", noSideEffect.}
|
||||
## Computes the `shift left` operation of `x` and `y`.
|
||||
proc `shl`*(x: uint8, y: SomeInteger): uint8 {.magic: "ShlI", noSideEffect.}
|
||||
proc `shl`*(x: uint16, y: SomeInteger): uint16 {.magic: "ShlI", noSideEffect.}
|
||||
proc `shl`*(x: uint32, y: SomeInteger): uint32 {.magic: "ShlI", noSideEffect.}
|
||||
proc `shl`*(x: uint64, y: SomeInteger): uint64 {.magic: "ShlI", noSideEffect.}
|
||||
|
||||
proc `and`*(x, y: uint): uint {.magic: "BitandI", noSideEffect.}
|
||||
## Computes the `bitwise and` of numbers `x` and `y`.
|
||||
proc `and`*(x, y: uint8): uint8 {.magic: "BitandI", noSideEffect.}
|
||||
proc `and`*(x, y: uint16): uint16 {.magic: "BitandI", noSideEffect.}
|
||||
proc `and`*(x, y: uint32): uint32 {.magic: "BitandI", noSideEffect.}
|
||||
proc `and`*(x, y: uint64): uint64 {.magic: "BitandI", noSideEffect.}
|
||||
|
||||
proc `or`*[T: SomeUnsignedInt](x, y: T): T {.magic: "BitorI", noSideEffect.}
|
||||
proc `or`*(x, y: uint): uint {.magic: "BitorI", noSideEffect.}
|
||||
## Computes the `bitwise or` of numbers `x` and `y`.
|
||||
proc `or`*(x, y: uint8): uint8 {.magic: "BitorI", noSideEffect.}
|
||||
proc `or`*(x, y: uint16): uint16 {.magic: "BitorI", noSideEffect.}
|
||||
proc `or`*(x, y: uint32): uint32 {.magic: "BitorI", noSideEffect.}
|
||||
proc `or`*(x, y: uint64): uint64 {.magic: "BitorI", noSideEffect.}
|
||||
|
||||
proc `xor`*[T: SomeUnsignedInt](x, y: T): T {.magic: "BitxorI", noSideEffect.}
|
||||
proc `xor`*(x, y: uint): uint {.magic: "BitxorI", noSideEffect.}
|
||||
## Computes the `bitwise xor` of numbers `x` and `y`.
|
||||
proc `xor`*(x, y: uint8): uint8 {.magic: "BitxorI", noSideEffect.}
|
||||
proc `xor`*(x, y: uint16): uint16 {.magic: "BitxorI", noSideEffect.}
|
||||
proc `xor`*(x, y: uint32): uint32 {.magic: "BitxorI", noSideEffect.}
|
||||
proc `xor`*(x, y: uint64): uint64 {.magic: "BitxorI", noSideEffect.}
|
||||
|
||||
proc `==`*[T: SomeUnsignedInt](x, y: T): bool {.magic: "EqI", noSideEffect.}
|
||||
proc `==`*(x, y: uint): bool {.magic: "EqI", noSideEffect.}
|
||||
## Compares two unsigned integers for equality.
|
||||
proc `==`*(x, y: uint8): bool {.magic: "EqI", noSideEffect.}
|
||||
proc `==`*(x, y: uint16): bool {.magic: "EqI", noSideEffect.}
|
||||
proc `==`*(x, y: uint32): bool {.magic: "EqI", noSideEffect.}
|
||||
proc `==`*(x, y: uint64): bool {.magic: "EqI", noSideEffect.}
|
||||
|
||||
proc `+`*[T: SomeUnsignedInt](x, y: T): T {.magic: "AddU", noSideEffect.}
|
||||
proc `+`*(x, y: uint): uint {.magic: "AddU", noSideEffect.}
|
||||
## Binary `+` operator for unsigned integers.
|
||||
proc `+`*(x, y: uint8): uint8 {.magic: "AddU", noSideEffect.}
|
||||
proc `+`*(x, y: uint16): uint16 {.magic: "AddU", noSideEffect.}
|
||||
proc `+`*(x, y: uint32): uint32 {.magic: "AddU", noSideEffect.}
|
||||
proc `+`*(x, y: uint64): uint64 {.magic: "AddU", noSideEffect.}
|
||||
|
||||
proc `-`*[T: SomeUnsignedInt](x, y: T): T {.magic: "SubU", noSideEffect.}
|
||||
proc `-`*(x, y: uint): uint {.magic: "SubU", noSideEffect.}
|
||||
## Binary `-` operator for unsigned integers.
|
||||
proc `-`*(x, y: uint8): uint8 {.magic: "SubU", noSideEffect.}
|
||||
proc `-`*(x, y: uint16): uint16 {.magic: "SubU", noSideEffect.}
|
||||
proc `-`*(x, y: uint32): uint32 {.magic: "SubU", noSideEffect.}
|
||||
proc `-`*(x, y: uint64): uint64 {.magic: "SubU", noSideEffect.}
|
||||
|
||||
proc `*`*[T: SomeUnsignedInt](x, y: T): T {.magic: "MulU", noSideEffect.}
|
||||
proc `*`*(x, y: uint): uint {.magic: "MulU", noSideEffect.}
|
||||
## Binary `*` operator for unsigned integers.
|
||||
proc `*`*(x, y: uint8): uint8 {.magic: "MulU", noSideEffect.}
|
||||
proc `*`*(x, y: uint16): uint16 {.magic: "MulU", noSideEffect.}
|
||||
proc `*`*(x, y: uint32): uint32 {.magic: "MulU", noSideEffect.}
|
||||
proc `*`*(x, y: uint64): uint64 {.magic: "MulU", noSideEffect.}
|
||||
|
||||
proc `div`*[T: SomeUnsignedInt](x, y: T): T {.magic: "DivU", noSideEffect.}
|
||||
proc `div`*(x, y: uint): uint {.magic: "DivU", noSideEffect.}
|
||||
## Computes the integer division for unsigned integers.
|
||||
## This is roughly the same as ``trunc(x/y)``.
|
||||
proc `div`*(x, y: uint8): uint8 {.magic: "DivU", noSideEffect.}
|
||||
proc `div`*(x, y: uint16): uint16 {.magic: "DivU", noSideEffect.}
|
||||
proc `div`*(x, y: uint32): uint32 {.magic: "DivU", noSideEffect.}
|
||||
proc `div`*(x, y: uint64): uint64 {.magic: "DivU", noSideEffect.}
|
||||
|
||||
proc `mod`*[T: SomeUnsignedInt](x, y: T): T {.magic: "ModU", noSideEffect.}
|
||||
proc `mod`*(x, y: uint): uint {.magic: "ModU", noSideEffect.}
|
||||
## Computes the integer modulo operation (remainder) for unsigned integers.
|
||||
## This is the same as ``x - (x div y) * y``.
|
||||
proc `mod`*(x, y: uint8): uint8 {.magic: "ModU", noSideEffect.}
|
||||
proc `mod`*(x, y: uint16): uint16 {.magic: "ModU", noSideEffect.}
|
||||
proc `mod`*(x, y: uint32): uint32 {.magic: "ModU", noSideEffect.}
|
||||
proc `mod`*(x, y: uint64): uint64 {.magic: "ModU", noSideEffect.}
|
||||
|
||||
proc `<=`*[T: SomeUnsignedInt](x, y: T): bool {.magic: "LeU", noSideEffect.}
|
||||
proc `<=`*(x, y: uint): bool {.magic: "LeU", noSideEffect.}
|
||||
## Returns true if ``x <= y``.
|
||||
proc `<=`*(x, y: uint8): bool {.magic: "LeU", noSideEffect.}
|
||||
proc `<=`*(x, y: uint16): bool {.magic: "LeU", noSideEffect.}
|
||||
proc `<=`*(x, y: uint32): bool {.magic: "LeU", noSideEffect.}
|
||||
proc `<=`*(x, y: uint64): bool {.magic: "LeU", noSideEffect.}
|
||||
|
||||
proc `<`*[T: SomeUnsignedInt](x, y: T): bool {.magic: "LtU", noSideEffect.}
|
||||
proc `<`*(x, y: uint): bool {.magic: "LtU", noSideEffect.}
|
||||
## Returns true if ``unsigned(x) < unsigned(y)``.
|
||||
proc `<`*(x, y: uint8): bool {.magic: "LtU", noSideEffect.}
|
||||
proc `<`*(x, y: uint16): bool {.magic: "LtU", noSideEffect.}
|
||||
proc `<`*(x, y: uint32): bool {.magic: "LtU", noSideEffect.}
|
||||
proc `<`*(x, y: uint64): bool {.magic: "LtU", noSideEffect.}
|
||||
|
||||
# floating point operations:
|
||||
proc `+`*(x: float32): float32 {.magic: "UnaryPlusF64", noSideEffect.}
|
||||
@@ -2074,10 +2111,10 @@ const hasAlloc = (hostOS != "standalone" or not defined(nogc)) and not defined(n
|
||||
|
||||
when not defined(JS) and not defined(nimscript) and hostOS != "standalone":
|
||||
include "system/cgprocs"
|
||||
when not defined(JS) and not defined(nimscript) and hasAlloc and not defined(gcDestructors):
|
||||
when not defined(JS) and not defined(nimscript) and hasAlloc and not defined(nimSeqsV2):
|
||||
proc addChar(s: NimString, c: char): NimString {.compilerproc, benign.}
|
||||
|
||||
when not defined(gcDestructors) or defined(nimscript):
|
||||
when not defined(nimSeqsV2) or defined(nimscript):
|
||||
proc add*[T](x: var seq[T], y: T) {.magic: "AppendSeqElem", noSideEffect.}
|
||||
## Generic proc for adding a data item `y` to a container `x`.
|
||||
##
|
||||
@@ -2104,7 +2141,7 @@ proc add*[T](x: var seq[T], y: openArray[T]) {.noSideEffect.} =
|
||||
setLen(x, xl + y.len)
|
||||
for i in 0..high(y): x[xl+i] = y[i]
|
||||
|
||||
when defined(gcDestructors):
|
||||
when defined(nimSeqsV2):
|
||||
template movingCopy(a, b) =
|
||||
a = move(b)
|
||||
else:
|
||||
@@ -3002,7 +3039,7 @@ proc `==`*[T](x, y: seq[T]): bool {.noSideEffect.} =
|
||||
else:
|
||||
when not defined(JS):
|
||||
proc seqToPtr[T](x: seq[T]): pointer {.inline, noSideEffect.} =
|
||||
when defined(gcDestructors):
|
||||
when defined(nimSeqsV2):
|
||||
result = cast[NimSeqV2[T]](x).p
|
||||
else:
|
||||
result = cast[pointer](x)
|
||||
@@ -3098,7 +3135,7 @@ when not defined(js):
|
||||
name: cstring
|
||||
PNimType = ptr TNimType
|
||||
|
||||
when defined(gcDestructors) and not defined(nimscript):
|
||||
when defined(nimSeqsV2) and not defined(nimscript):
|
||||
include "core/strs"
|
||||
include "core/seqs"
|
||||
|
||||
@@ -3747,7 +3784,7 @@ when not defined(JS): #and not defined(nimscript):
|
||||
{.pop.}
|
||||
{.push stack_trace: off, profiler:off.}
|
||||
when hasAlloc:
|
||||
when not defined(gcDestructors):
|
||||
when not defined(nimSeqsV2):
|
||||
include "system/sysstr"
|
||||
{.pop.}
|
||||
when hasAlloc: include "system/strmantle"
|
||||
@@ -4162,7 +4199,7 @@ proc shallow*(s: var string) {.noSideEffect, inline.} =
|
||||
## perform deep copies of `s`.
|
||||
##
|
||||
## This is only useful for optimization purposes.
|
||||
when not defined(JS) and not defined(nimscript) and not defined(gcDestructors):
|
||||
when not defined(JS) and not defined(nimscript) and not defined(nimSeqsV2):
|
||||
var s = cast[PGenericSeq](s)
|
||||
if s == nil:
|
||||
s = cast[PGenericSeq](newString(0))
|
||||
|
||||
@@ -439,7 +439,7 @@ proc getStackTrace(e: ref Exception): string =
|
||||
proc getStackTraceEntries*(e: ref Exception): seq[StackTraceEntry] =
|
||||
## Returns the attached stack trace to the exception ``e`` as
|
||||
## a ``seq``. This is not yet available for the JS backend.
|
||||
when not defined(gcDestructors):
|
||||
when not defined(nimSeqsV2):
|
||||
shallowCopy(result, e.trace)
|
||||
else:
|
||||
result = move(e.trace)
|
||||
|
||||
@@ -254,7 +254,7 @@ proc forAllChildren(cell: PCell, op: WalkOp) =
|
||||
of tyRef: # common case
|
||||
forAllChildrenAux(cellToUsr(cell), cell.typ.base, op)
|
||||
of tySequence:
|
||||
when not defined(gcDestructors):
|
||||
when not defined(nimSeqsV2):
|
||||
var d = cast[ByteAddress](cellToUsr(cell))
|
||||
var s = cast[PGenericSeq](d)
|
||||
if s != nil:
|
||||
@@ -304,7 +304,7 @@ proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} =
|
||||
zeroMem(result, size)
|
||||
when defined(memProfiler): nimProfile(size)
|
||||
|
||||
when not defined(gcDestructors):
|
||||
when not defined(nimSeqsV2):
|
||||
proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} =
|
||||
# `newObj` already uses locks, so no need for them here.
|
||||
let size = addInt(mulInt(len, typ.base.size), GenericSeqSize)
|
||||
|
||||
@@ -112,6 +112,8 @@ proc c_setvbuf(f: File, buf: pointer, mode: cint, size: csize): cint {.
|
||||
|
||||
proc c_fprintf(f: File, frmt: cstring): cint {.
|
||||
importc: "fprintf", header: "<stdio.h>", varargs, discardable.}
|
||||
proc c_fputc(c: char, f: File): cint {.
|
||||
importc: "fputc", header: "<stdio.h>".}
|
||||
|
||||
## When running nim in android app stdout goes no where, so echo gets ignored
|
||||
## To redreict echo to the android logcat use -d:androidNDK
|
||||
@@ -212,6 +214,10 @@ when defined(windows):
|
||||
var i = c_fprintf(f, "%s", s)
|
||||
while i < s.len:
|
||||
if s[i] == '\0':
|
||||
let w = c_fputc('\0', f)
|
||||
if w != 0:
|
||||
if doRaise: raiseEIO("cannot write string to file")
|
||||
break
|
||||
inc i
|
||||
else:
|
||||
let w = c_fprintf(f, "%s", unsafeAddr s[i])
|
||||
@@ -653,10 +659,10 @@ when defined(windows) and appType == "console" and
|
||||
|
||||
proc readFile*(filename: string): TaintedString {.tags: [ReadIOEffect], benign.} =
|
||||
## Opens a file named `filename` for reading, calls `readAll
|
||||
## <#readAll>`_ and closes the file afterwards. Returns the string.
|
||||
## Raises an IO exception in case of an error. If # you need to call
|
||||
## <#readAll,File>`_ and closes the file afterwards. Returns the string.
|
||||
## Raises an IO exception in case of an error. If you need to call
|
||||
## this inside a compile time macro you can use `staticRead
|
||||
## <#staticRead>`_.
|
||||
## <system.html#staticRead,string>`_.
|
||||
var f: File
|
||||
if open(f, filename):
|
||||
try:
|
||||
|
||||
@@ -152,7 +152,9 @@ when defined(boehmgc):
|
||||
proc nimGC_setStackBottom(theStackBottom: pointer) = discard
|
||||
|
||||
proc initGC() =
|
||||
boehmGC_set_all_interior_pointers(0)
|
||||
when defined(boehmNoIntPtr):
|
||||
# See #12286
|
||||
boehmGC_set_all_interior_pointers(0)
|
||||
boehmGCinit()
|
||||
when hasThreadSupport:
|
||||
boehmGC_allow_register_threads()
|
||||
@@ -516,7 +518,7 @@ else:
|
||||
else:
|
||||
include "system/gc"
|
||||
|
||||
when not declared(nimNewSeqOfCap) and not defined(gcDestructors):
|
||||
when not declared(nimNewSeqOfCap) and not defined(nimSeqsV2):
|
||||
proc nimNewSeqOfCap(typ: PNimType, cap: int): pointer {.compilerproc.} =
|
||||
when defined(gcRegions):
|
||||
let s = mulInt(cap, typ.base.size) # newStr already adds GenericSeqSize
|
||||
|
||||
@@ -160,7 +160,7 @@ when not defined(useNimRtl):
|
||||
reprAux(result, cast[pointer](cast[ByteAddress](p) + i*bs), typ.base, cl)
|
||||
add result, "]"
|
||||
|
||||
when defined(gcDestructors):
|
||||
when defined(nimSeqsV2):
|
||||
type
|
||||
GenericSeq = object
|
||||
len: int
|
||||
|
||||
@@ -849,6 +849,20 @@ function main() {
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<a class="reference reference-toplevel" href="#10" id="60">Consts</a>
|
||||
<ul class="simple simple-toc-section">
|
||||
<li><a class="reference" href="#C_A"
|
||||
title="C_A = 0x7FF0000000000000'f64"><wbr />C_<wbr />A<span class="attachedType"></span></a></li>
|
||||
<li><a class="reference" href="#C_B"
|
||||
title="C_B = 0o377'i8"><wbr />C_<wbr />B<span class="attachedType"></span></a></li>
|
||||
<li><a class="reference" href="#C_C"
|
||||
title="C_C = 0o277'i8"><wbr />C_<wbr />C<span class="attachedType"></span></a></li>
|
||||
<li><a class="reference" href="#C_D"
|
||||
title="C_D = 0o177777'i16"><wbr />C_<wbr />D<span class="attachedType"></span></a></li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<a class="reference reference-toplevel" href="#12" id="62">Procs</a>
|
||||
<ul class="simple simple-toc-section">
|
||||
@@ -938,6 +952,39 @@ The enum B.
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
|
||||
</dl></div>
|
||||
<div class="section" id="10">
|
||||
<h1><a class="toc-backref" href="#10">Consts</a></h1>
|
||||
<dl class="item">
|
||||
<a id="C_A"></a>
|
||||
<dt><pre><a href="testproject.html#C_A"><span class="Identifier">C_A</span></a> <span class="Other">=</span> <span class="FloatNumber">0x7FF0000000000000'f64</span></pre></dt>
|
||||
<dd>
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<a id="C_B"></a>
|
||||
<dt><pre><a href="testproject.html#C_B"><span class="Identifier">C_B</span></a> <span class="Other">=</span> <span class="DecNumber">0o377'i8</span></pre></dt>
|
||||
<dd>
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<a id="C_C"></a>
|
||||
<dt><pre><a href="testproject.html#C_C"><span class="Identifier">C_C</span></a> <span class="Other">=</span> <span class="DecNumber">0o277'i8</span></pre></dt>
|
||||
<dd>
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<a id="C_D"></a>
|
||||
<dt><pre><a href="testproject.html#C_D"><span class="Identifier">C_D</span></a> <span class="Other">=</span> <span class="DecNumber">0o177777'i16</span></pre></dt>
|
||||
<dd>
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
|
||||
</dl></div>
|
||||
|
||||
@@ -840,6 +840,22 @@ function main() {
|
||||
<li><a class="reference external"
|
||||
data-doc-search-tag="testproject: buzz[T](a, b: T): T" href="testproject.html#buzz%2CT%2CT">testproject: buzz[T](a, b: T): T</a></li>
|
||||
</ul></dd>
|
||||
<dt><a name="C_A" href="#C_A"><span>C_A:</span></a></dt><dd><ul class="simple">
|
||||
<li><a class="reference external"
|
||||
data-doc-search-tag="testproject: C_A" href="testproject.html#C_A">testproject: C_A</a></li>
|
||||
</ul></dd>
|
||||
<dt><a name="C_B" href="#C_B"><span>C_B:</span></a></dt><dd><ul class="simple">
|
||||
<li><a class="reference external"
|
||||
data-doc-search-tag="testproject: C_B" href="testproject.html#C_B">testproject: C_B</a></li>
|
||||
</ul></dd>
|
||||
<dt><a name="C_C" href="#C_C"><span>C_C:</span></a></dt><dd><ul class="simple">
|
||||
<li><a class="reference external"
|
||||
data-doc-search-tag="testproject: C_C" href="testproject.html#C_C">testproject: C_C</a></li>
|
||||
</ul></dd>
|
||||
<dt><a name="C_D" href="#C_D"><span>C_D:</span></a></dt><dd><ul class="simple">
|
||||
<li><a class="reference external"
|
||||
data-doc-search-tag="testproject: C_D" href="testproject.html#C_D">testproject: C_D</a></li>
|
||||
</ul></dd>
|
||||
<dt><a name="enumValueA" href="#enumValueA"><span>enumValueA:</span></a></dt><dd><ul class="simple">
|
||||
<li><a class="reference external"
|
||||
data-doc-search-tag="SomeType.enumValueA" href="subdir/subdir_b/utils.html#enumValueA">SomeType.enumValueA</a></li>
|
||||
|
||||
@@ -9,6 +9,11 @@ runnableExamples:
|
||||
# bug #11078
|
||||
for x in "xx": discard
|
||||
|
||||
const
|
||||
C_A* = 0x7FF0000000000000'f64
|
||||
C_B* = 0o377'i8
|
||||
C_C* = 0o277'i8
|
||||
C_D* = 0o177777'i16
|
||||
|
||||
template foo*(a, b: SomeType) =
|
||||
## This does nothing
|
||||
@@ -31,7 +36,7 @@ import std/macros
|
||||
macro bar*(): untyped =
|
||||
result = newStmtList()
|
||||
|
||||
var aVariable*: array[1,int]
|
||||
var aVariable*: array[1, int]
|
||||
|
||||
aEnum()
|
||||
bEnum()
|
||||
|
||||
@@ -48,6 +48,7 @@ Options:
|
||||
--maxresults:N limit the number of suggestions to N
|
||||
--tester implies --stdin and outputs a line
|
||||
'""" & DummyEof & """' for the tester
|
||||
--find attempts to find the project file of the current project
|
||||
|
||||
The server then listens to the connection and takes line-based commands.
|
||||
|
||||
@@ -92,7 +93,7 @@ proc myLog(s: string) =
|
||||
|
||||
const
|
||||
seps = {':', ';', ' ', '\t'}
|
||||
Help = "usage: sug|con|def|use|dus|chk|mod|highlight|outline|known file.nim[;dirtyfile.nim]:line:col\n" &
|
||||
Help = "usage: sug|con|def|use|dus|chk|mod|highlight|outline|known|project file.nim[;dirtyfile.nim]:line:col\n" &
|
||||
"type 'quit' to quit\n" &
|
||||
"type 'debug' to toggle debug mode on/off\n" &
|
||||
"type 'terse' to toggle terse mode on/off"
|
||||
@@ -244,6 +245,7 @@ proc toStdout() {.gcsafe.} =
|
||||
of ideNone: break
|
||||
of ideMsg: echo res.doc
|
||||
of ideKnown: echo res.quality == 1
|
||||
of ideProject: echo res.filePath
|
||||
else: echo res
|
||||
|
||||
proc toSocket(stdoutSocket: Socket) {.gcsafe.} =
|
||||
@@ -253,6 +255,7 @@ proc toSocket(stdoutSocket: Socket) {.gcsafe.} =
|
||||
of ideNone: break
|
||||
of ideMsg: stdoutSocket.send(res.doc & "\c\L")
|
||||
of ideKnown: stdoutSocket.send($(res.quality == 1) & "\c\L")
|
||||
of ideProject: stdoutSocket.send(res.filePath & "\c\L")
|
||||
else: stdoutSocket.send($res & "\c\L")
|
||||
|
||||
proc toEpc(client: Socket; uid: BiggestInt) {.gcsafe.} =
|
||||
@@ -265,6 +268,8 @@ proc toEpc(client: Socket; uid: BiggestInt) {.gcsafe.} =
|
||||
list.add sexp(res.doc)
|
||||
of ideKnown:
|
||||
list.add sexp(res.quality == 1)
|
||||
of ideProject:
|
||||
list.add sexp(res.filePath)
|
||||
else:
|
||||
list.add sexp(res)
|
||||
returnEpc(client, uid, list)
|
||||
@@ -319,8 +324,8 @@ proc replTcp(x: ThreadParams) {.thread.} =
|
||||
server.bindAddr(x.port, x.address)
|
||||
server.listen()
|
||||
var inp = "".TaintedString
|
||||
var stdoutSocket: Socket
|
||||
while true:
|
||||
var stdoutSocket = newSocket()
|
||||
accept(server, stdoutSocket)
|
||||
|
||||
stdoutSocket.readLine(inp)
|
||||
@@ -353,7 +358,7 @@ proc replEpc(x: ThreadParams) {.thread.} =
|
||||
echo port
|
||||
stdout.flushFile()
|
||||
|
||||
var client = newSocket()
|
||||
var client: Socket
|
||||
# Wait for connection
|
||||
accept(server, client)
|
||||
while true:
|
||||
@@ -434,6 +439,7 @@ proc execCmd(cmd: string; graph: ModuleGraph; cachedMsgs: CachedMsgs) =
|
||||
of "debug": toggle optIdeDebug
|
||||
of "terse": toggle optIdeTerse
|
||||
of "known": conf.ideCmd = ideKnown
|
||||
of "project": conf.ideCmd = ideProject
|
||||
else: err()
|
||||
var dirtyfile = ""
|
||||
var orig = ""
|
||||
@@ -453,6 +459,8 @@ proc execCmd(cmd: string; graph: ModuleGraph; cachedMsgs: CachedMsgs) =
|
||||
|
||||
if conf.ideCmd == ideKnown:
|
||||
results.send(Suggest(section: ideKnown, quality: ord(fileInfoKnown(conf, AbsoluteFile orig))))
|
||||
elif conf.ideCmd == ideProject:
|
||||
results.send(Suggest(section: ideProject, filePath: string conf.projectFull))
|
||||
else:
|
||||
if conf.ideCmd == ideChk:
|
||||
for cm in cachedMsgs: errorHook(conf, cm.info, cm.msg, cm.sev)
|
||||
@@ -549,6 +557,7 @@ proc mainCommand(graph: ModuleGraph) =
|
||||
|
||||
proc processCmdLine*(pass: TCmdLinePass, cmd: string; conf: ConfigRef) =
|
||||
var p = parseopt.initOptParser(cmd)
|
||||
var findProject = false
|
||||
while true:
|
||||
parseopt.next(p)
|
||||
case p.kind
|
||||
@@ -594,6 +603,8 @@ proc processCmdLine*(pass: TCmdLinePass, cmd: string; conf: ConfigRef) =
|
||||
gRefresh = true
|
||||
of "maxresults":
|
||||
conf.suggestMaxResults = parseInt(p.val)
|
||||
of "find":
|
||||
findProject = true
|
||||
else: processSwitch(pass, p, conf)
|
||||
of cmdArgument:
|
||||
let a = unixToNativePath(p.key)
|
||||
@@ -602,7 +613,12 @@ proc processCmdLine*(pass: TCmdLinePass, cmd: string; conf: ConfigRef) =
|
||||
# don't make it worse, report the error the old way:
|
||||
if conf.projectName.len == 0: conf.projectName = a
|
||||
else:
|
||||
conf.projectName = a
|
||||
if findProject:
|
||||
conf.projectName = findProjectNimFile(conf, a.parentDir())
|
||||
if conf.projectName.len == 0:
|
||||
conf.projectName = a
|
||||
else:
|
||||
conf.projectName = a
|
||||
# if processArgument(pass, p, argsCount): break
|
||||
|
||||
proc handleCmdLine(cache: IdentCache; conf: ConfigRef) =
|
||||
@@ -732,6 +748,8 @@ else:
|
||||
stderr.write s & "\n"
|
||||
if conf.ideCmd == ideKnown:
|
||||
retval.add(Suggest(section: ideKnown, quality: ord(fileInfoKnown(conf, file))))
|
||||
elif conf.ideCmd == ideProject:
|
||||
retval.add(Suggest(section: ideProject, filePath: string conf.projectFull))
|
||||
else:
|
||||
if conf.ideCmd == ideChk:
|
||||
for cm in nimsuggest.cachedMsgs: errorHook(conf, cm.info, cm.msg, cm.sev)
|
||||
|
||||
27
readme.md
27
readme.md
@@ -60,29 +60,20 @@ the installation instructions on the website to do so: https://nim-lang.org/inst
|
||||
|
||||
For package maintainers: see [packaging guidelines](https://nim-lang.github.io/Nim/packaging.html).
|
||||
|
||||
|
||||
First get Nim from github:
|
||||
|
||||
```
|
||||
# step 1:
|
||||
git clone https://github.com/nim-lang/Nim.git
|
||||
cd Nim
|
||||
|
||||
# step 2 (posix) clones `csources.git`, bootstraps Nim compiler and compiles tools
|
||||
sh build_all.sh
|
||||
|
||||
# step 2 (windows)
|
||||
git clone --depth 1 https://github.com/nim-lang/csources.git
|
||||
|
||||
cd csources
|
||||
# requires `gcc` in your PATH, see also https://nim-lang.org/install_windows.html
|
||||
build.bat # x86 Windows
|
||||
build64.bat # x86_64 Windows
|
||||
cd ..
|
||||
|
||||
bin\nim c koch
|
||||
koch boot -d:release
|
||||
koch tools # Compile Nimble and other tools
|
||||
# end of step 2 (windows)
|
||||
```
|
||||
|
||||
Next run the appropriate build shell script for your platform:
|
||||
|
||||
* `build_all.sh` (Linux, Mac)
|
||||
* `build_all.bat` (Windows)
|
||||
|
||||
|
||||
Finally, once you have finished the build steps (on Windows, Mac or Linux) you
|
||||
should add the ``bin`` directory to your PATH.
|
||||
|
||||
|
||||
@@ -37,8 +37,8 @@ pkg "gnuplot"
|
||||
pkg "hts", "nim c -o:htss -r src/hts.nim"
|
||||
pkg "illwill", "nimble examples"
|
||||
pkg "inim"
|
||||
pkg "itertools", "nim doc src/itertools.nim"
|
||||
pkg "iterutils"
|
||||
# pkg "itertools", "nim doc src/itertools.nim"
|
||||
# pkg "iterutils"
|
||||
pkg "jstin"
|
||||
pkg "karax", "nim c -r tests/tester.nim"
|
||||
pkg "loopfusion"
|
||||
|
||||
@@ -4,6 +4,14 @@ int32
|
||||
int32
|
||||
1280
|
||||
1280
|
||||
3
|
||||
1
|
||||
2
|
||||
2
|
||||
3
|
||||
4294967295
|
||||
2
|
||||
0
|
||||
'''
|
||||
"""
|
||||
|
||||
@@ -145,3 +153,17 @@ block tsubrange:
|
||||
|
||||
level = min(level + 2, maxLevel).n16
|
||||
doAssert level == 1
|
||||
|
||||
block tissue12177:
|
||||
var a: uint16 = 1
|
||||
var b: uint32 = 2
|
||||
|
||||
echo(b + a)
|
||||
echo(b - a)
|
||||
echo(b * a)
|
||||
echo(b div a)
|
||||
|
||||
echo(a + b)
|
||||
echo(a - b)
|
||||
echo(a * b)
|
||||
echo(a div b)
|
||||
|
||||
@@ -582,3 +582,11 @@ template append2*(args: varargs[string, myAppend]): string =
|
||||
|
||||
let foo = append2("1", "2", "3")
|
||||
echo foo
|
||||
|
||||
block t12466:
|
||||
# https://github.com/nim-lang/Nim/issues/12466
|
||||
var a: array[288, uint16]
|
||||
for i in 0'u16 ..< 144'u16:
|
||||
a[0'u16 + i] = i
|
||||
for i in 0'u16 ..< 8'u16:
|
||||
a[0'u16 + i] = i
|
||||
|
||||
@@ -43,4 +43,14 @@ block: #8399
|
||||
|
||||
waitFor foo("$asd")
|
||||
|
||||
block: # nkCheckedFieldExpr
|
||||
proc bar(): Future[JsonNode] {.async.} =
|
||||
return newJInt(5)
|
||||
|
||||
proc foo() {.async.} =
|
||||
let n = 10 + (await bar()).num
|
||||
doAssert(n == 15)
|
||||
|
||||
waitFor foo()
|
||||
|
||||
echo "ok"
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
discard """
|
||||
output: '''(1, 1)
|
||||
(2, 2)
|
||||
(3, 3)
|
||||
@[1, 2, 3, 4]
|
||||
'''
|
||||
"""
|
||||
|
||||
iterator zip[T1, T2](a: openarray[T1], b: openarray[T2]): iterator() {.inline.} =
|
||||
let len = min(a.len, b.len)
|
||||
for i in 0..<len:
|
||||
echo (a[i], b[i])
|
||||
|
||||
proc foo(args: varargs[int]) =
|
||||
for i in zip(args,args):
|
||||
discard
|
||||
|
||||
foo(1,2,3)
|
||||
|
||||
# 10999
|
||||
|
||||
proc varargsToSeq(vals: varargs[int32]): seq[int32] =
|
||||
result = newSeqOfCap[int32](vals.len)
|
||||
for v in vals:
|
||||
result.add v
|
||||
|
||||
echo varargsToSeq(1, 2, 3, 4)
|
||||
14
tests/ccgbugs/tccgissues.nim
Normal file
14
tests/ccgbugs/tccgissues.nim
Normal file
@@ -0,0 +1,14 @@
|
||||
discard """
|
||||
output: '''
|
||||
@[1, 2, 3, 4]
|
||||
'''
|
||||
"""
|
||||
|
||||
# issue #10999
|
||||
|
||||
proc varargsToSeq(vals: varargs[int32]): seq[int32] =
|
||||
result = newSeqOfCap[int32](vals.len)
|
||||
for v in vals:
|
||||
result.add v
|
||||
|
||||
echo varargsToSeq(1, 2, 3, 4)
|
||||
@@ -233,7 +233,7 @@ block tablesref:
|
||||
for y in 0..1:
|
||||
assert t[(x,y)] == $x & $y
|
||||
assert($t ==
|
||||
"{(x: 0, y: 1): \"01\", (x: 0, y: 0): \"00\", (x: 1, y: 0): \"10\", (x: 1, y: 1): \"11\"}")
|
||||
"{(x: 1, y: 1): \"11\", (x: 0, y: 0): \"00\", (x: 0, y: 1): \"01\", (x: 1, y: 0): \"10\"}")
|
||||
|
||||
block tableTest2:
|
||||
var t = newTable[string, float]()
|
||||
@@ -340,7 +340,7 @@ block tablesref:
|
||||
block anonZipTest:
|
||||
let keys = @['a','b','c']
|
||||
let values = @[1, 2, 3]
|
||||
doAssert "{'a': 1, 'b': 2, 'c': 3}" == $ toTable zip(keys, values)
|
||||
doAssert "{'c': 3, 'a': 1, 'b': 2}" == $ toTable zip(keys, values)
|
||||
|
||||
block clearTableTest:
|
||||
var t = newTable[string, float]()
|
||||
|
||||
@@ -48,7 +48,7 @@ block tableTest1:
|
||||
for y in 0..1:
|
||||
assert t[(x,y)] == $x & $y
|
||||
assert($t ==
|
||||
"{(x: 0, y: 1): \"01\", (x: 0, y: 0): \"00\", (x: 1, y: 0): \"10\", (x: 1, y: 1): \"11\"}")
|
||||
"{(x: 1, y: 1): \"11\", (x: 0, y: 0): \"00\", (x: 0, y: 1): \"01\", (x: 1, y: 0): \"10\"}")
|
||||
|
||||
block tableTest2:
|
||||
var t = initTable[string, float]()
|
||||
|
||||
@@ -9,23 +9,23 @@ proc test(foo: Foo[int])
|
||||
required type for foo: Foo[int]
|
||||
but expression 'bar' is of type: Bar[system.int]
|
||||
t3330.nim(63, 8) Hint: Non-matching candidates for add(k, string, T)
|
||||
proc add[T](x: var seq[T]; y: openArray[T])
|
||||
first type mismatch at position: 1
|
||||
required type for x: var seq[T]
|
||||
but expression 'k' is of type: Alias
|
||||
proc add[T](x: var seq[T]; y: T)
|
||||
first type mismatch at position: 1
|
||||
required type for x: var seq[T]
|
||||
but expression 'k' is of type: Alias
|
||||
proc add(x: var string; y: string)
|
||||
first type mismatch at position: 1
|
||||
required type for x: var string
|
||||
but expression 'k' is of type: Alias
|
||||
proc add(result: var string; x: float)
|
||||
first type mismatch at position: 1
|
||||
required type for result: var string
|
||||
but expression 'k' is of type: Alias
|
||||
proc add(x: var string; y: cstring)
|
||||
proc add(x: var string; y: char)
|
||||
first type mismatch at position: 1
|
||||
required type for x: var string
|
||||
but expression 'k' is of type: Alias
|
||||
proc add(x: var string; y: string)
|
||||
proc add(x: var string; y: cstring)
|
||||
first type mismatch at position: 1
|
||||
required type for x: var string
|
||||
but expression 'k' is of type: Alias
|
||||
@@ -33,9 +33,9 @@ proc add(result: var string; x: int64)
|
||||
first type mismatch at position: 1
|
||||
required type for result: var string
|
||||
but expression 'k' is of type: Alias
|
||||
proc add(x: var string; y: char)
|
||||
proc add[T](x: var seq[T]; y: openArray[T])
|
||||
first type mismatch at position: 1
|
||||
required type for x: var string
|
||||
required type for x: var seq[T]
|
||||
but expression 'k' is of type: Alias
|
||||
|
||||
t3330.nim(63, 8) template/generic instantiation of `add` from here
|
||||
|
||||
28
tests/errmsgs/ttypeAllowed.nim
Normal file
28
tests/errmsgs/ttypeAllowed.nim
Normal file
@@ -0,0 +1,28 @@
|
||||
discard """
|
||||
cmd: "nim check $file"
|
||||
errmsg: ""
|
||||
nimout: '''
|
||||
ttypeAllowed.nim(13, 5) Error: invalid type: 'iterator (a: int, b: int, step: Positive): int{.inline, noSideEffect, gcsafe, locks: 0.}' for let
|
||||
ttypeAllowed.nim(17, 7) Error: invalid type for const: iterator (a: int, b: int, step: Positive): int{.inline, noSideEffect, gcsafe, locks: 0.}
|
||||
ttypeAllowed.nim(21, 5) Error: invalid type: 'iterator (a: int, b: int, step: Positive): int{.inline, noSideEffect, gcsafe, locks: 0.}' for var
|
||||
ttypeAllowed.nim(26, 10) Error: invalid type: 'iterator (a: int, b: int, step: Positive): int{.inline, noSideEffect, gcsafe, locks: 0.}' for result
|
||||
'''
|
||||
"""
|
||||
|
||||
|
||||
let f1 = case true
|
||||
of true: countup[int]
|
||||
of false: countdown[int]
|
||||
|
||||
const f2 = case true
|
||||
of true: countup[int]
|
||||
of false: countdown[int]
|
||||
|
||||
var f3 = case true
|
||||
of true: countup[int]
|
||||
of false: countdown[int]
|
||||
|
||||
proc foobar(): auto =
|
||||
result = case true
|
||||
of true: countup[int]
|
||||
of false: countdown[int]
|
||||
@@ -4,14 +4,14 @@ line: 22
|
||||
nimout: '''
|
||||
twrong_at_operator.nim(22, 30) Error: type mismatch: got <array[0..0, type int]>
|
||||
but expected one of:
|
||||
proc `@`[T](a: openArray[T]): seq[T]
|
||||
first type mismatch at position: 1
|
||||
required type for a: openArray[T]
|
||||
but expression '[int]' is of type: array[0..0, type int]
|
||||
proc `@`[IDX, T](a: sink array[IDX, T]): seq[T]
|
||||
first type mismatch at position: 1
|
||||
required type for a: sink array[IDX, T]
|
||||
but expression '[int]' is of type: array[0..0, type int]
|
||||
proc `@`[T](a: openArray[T]): seq[T]
|
||||
first type mismatch at position: 1
|
||||
required type for a: openArray[T]
|
||||
but expression '[int]' is of type: array[0..0, type int]
|
||||
|
||||
expression: @[int]
|
||||
'''
|
||||
|
||||
73
tests/generics/tforwardgenericconstrained.nim
Normal file
73
tests/generics/tforwardgenericconstrained.nim
Normal file
@@ -0,0 +1,73 @@
|
||||
discard """
|
||||
output: '''
|
||||
hello some integer
|
||||
hello range
|
||||
hello tuple
|
||||
hello seq
|
||||
hello object
|
||||
hello distinct
|
||||
hello enum
|
||||
'''
|
||||
"""
|
||||
|
||||
|
||||
|
||||
# SomeInteger
|
||||
|
||||
proc foo[T : SomeInteger](arg: T)
|
||||
proc foo[T : SomeInteger](arg: T) =
|
||||
echo "hello some integer"
|
||||
foo(123)
|
||||
|
||||
# range
|
||||
|
||||
proc foo2[T : range[0..100]](arg: T)
|
||||
proc foo2[T : range[0..100]](arg: T) =
|
||||
echo "hello range"
|
||||
foo2(7)
|
||||
|
||||
# tuple
|
||||
|
||||
proc foo3[T : tuple](arg: T)
|
||||
proc foo3[T : tuple](arg: T) =
|
||||
echo "hello tuple"
|
||||
|
||||
foo3((a:123,b:321))
|
||||
|
||||
# seq
|
||||
|
||||
proc foo4[T: seq](arg: T)
|
||||
proc foo4[T: seq](arg: T) =
|
||||
echo "hello seq"
|
||||
|
||||
foo4(@[1,2,3])
|
||||
|
||||
# object
|
||||
|
||||
proc foo5[T : object](arg: T)
|
||||
proc foo5[T : object](arg: T) =
|
||||
echo "hello object"
|
||||
|
||||
type MyType = object
|
||||
var mt: MyType
|
||||
foo5(mt)
|
||||
|
||||
# distinct
|
||||
|
||||
proc foo6[T : distinct](arg: T)
|
||||
proc foo6[T : distinct](arg: T) =
|
||||
echo "hello distinct"
|
||||
|
||||
type MyDistinct = distinct string
|
||||
var md: MyDistinct
|
||||
foo6(md)
|
||||
|
||||
# enum
|
||||
|
||||
proc foo7[T : enum](arg: T)
|
||||
proc foo7[T : enum](arg: T) =
|
||||
echo "hello enum"
|
||||
|
||||
type MyEnum = enum
|
||||
ValueA
|
||||
foo7(ValueA)
|
||||
@@ -762,11 +762,3 @@ block t3717:
|
||||
|
||||
var f: Foo[Foo[int]]
|
||||
discard foo(f)
|
||||
|
||||
|
||||
|
||||
block t5707:
|
||||
proc foo[T]: seq[int] =
|
||||
return lc[x | (x <- 1..10, x mod 2 == 0), int]
|
||||
|
||||
doAssert foo[float32]() == @[2, 4, 6, 8, 10]
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
discard """
|
||||
errormsg: "inline iterators are not first-class / cannot be assigned to variables"
|
||||
line: 8
|
||||
"""
|
||||
|
||||
iterator foo: int =
|
||||
yield 2
|
||||
let x = foo
|
||||
2
tests/modules/a/utils.nim
Normal file
2
tests/modules/a/utils.nim
Normal file
@@ -0,0 +1,2 @@
|
||||
proc burnMem*(a: int) =
|
||||
discard
|
||||
2
tests/modules/b/utils.nim
Normal file
2
tests/modules/b/utils.nim
Normal file
@@ -0,0 +1,2 @@
|
||||
# module b/utils.nim
|
||||
let x* = 10
|
||||
5
tests/modules/tutils_ab.nim
Normal file
5
tests/modules/tutils_ab.nim
Normal file
@@ -0,0 +1,5 @@
|
||||
import a/utils as autils, b/utils
|
||||
|
||||
# bug #12420
|
||||
|
||||
burnMem(x)
|
||||
@@ -1,6 +1,7 @@
|
||||
discard """
|
||||
errormsg: '''ambiguous call; both foobar.async'''
|
||||
line: 9
|
||||
line: 10
|
||||
disabled: "32bit"
|
||||
"""
|
||||
|
||||
import foobar
|
||||
|
||||
44
tests/stdlib/tbase64.nim
Normal file
44
tests/stdlib/tbase64.nim
Normal file
@@ -0,0 +1,44 @@
|
||||
discard """
|
||||
output: "OK"
|
||||
"""
|
||||
import base64
|
||||
|
||||
proc main() =
|
||||
doAssert encode("Hello World") == "SGVsbG8gV29ybGQ="
|
||||
doAssert encode("leasure.") == "bGVhc3VyZS4="
|
||||
doAssert encode("easure.") == "ZWFzdXJlLg=="
|
||||
doAssert encode("asure.") == "YXN1cmUu"
|
||||
doAssert encode("sure.") == "c3VyZS4="
|
||||
doAssert encode([1,2,3]) == "AQID"
|
||||
doAssert encode(['h','e','y']) == "aGV5"
|
||||
|
||||
doAssert encode("") == ""
|
||||
doAssert decode("") == ""
|
||||
|
||||
const testInputExpandsTo76 = "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++"
|
||||
const testInputExpands = "++++++++++++++++++++++++++++++"
|
||||
const longText = """Man is distinguished, not only by his reason, but by this
|
||||
singular passion from other animals, which is a lust of the mind,
|
||||
that by a perseverance of delight in the continued and indefatigable
|
||||
generation of knowledge, exceeds the short vehemence of any carnal
|
||||
pleasure."""
|
||||
const tests = ["", "abc", "xyz", "man", "leasure.", "sure.", "easure.",
|
||||
"asure.", longText, testInputExpandsTo76, testInputExpands]
|
||||
|
||||
doAssert encodeMIME("foobarbaz", lineLen=4) == "Zm9v\r\nYmFy\r\nYmF6"
|
||||
doAssert decode("Zm9v\r\nYmFy\r\nYmF6") == "foobarbaz"
|
||||
|
||||
for t in items(tests):
|
||||
doAssert decode(encode(t)) == t
|
||||
doAssert decode(encodeMIME(t, lineLen=40)) == t
|
||||
doAssert decode(encodeMIME(t, lineLen=76)) == t
|
||||
|
||||
const invalid = "SGVsbG\x008gV29ybGQ="
|
||||
try:
|
||||
doAssert decode(invalid) == "will throw error"
|
||||
except ValueError:
|
||||
discard
|
||||
|
||||
echo "OK"
|
||||
|
||||
main()
|
||||
@@ -1,6 +1,8 @@
|
||||
discard """
|
||||
output: ""
|
||||
targets: "c js"
|
||||
"""
|
||||
|
||||
import json, strutils, options, tables
|
||||
|
||||
when true:
|
||||
@@ -18,6 +20,11 @@ when true:
|
||||
case kind*: ReplayEventKind
|
||||
of FoodAppeared, FoodEaten:
|
||||
foodPos*: Point[float]
|
||||
case subKind*: bool
|
||||
of true:
|
||||
it: int
|
||||
of false:
|
||||
ot: float
|
||||
of DirectionChanged:
|
||||
playerPos*: float
|
||||
|
||||
@@ -33,7 +40,9 @@ when true:
|
||||
ReplayEvent(
|
||||
time: 1.2345,
|
||||
kind: FoodEaten,
|
||||
foodPos: Point[float](x: 5.0, y: 1.0)
|
||||
foodPos: Point[float](x: 5.0, y: 1.0),
|
||||
subKind: true,
|
||||
it: 7
|
||||
)
|
||||
],
|
||||
test: 18827361,
|
||||
@@ -592,3 +601,34 @@ static:
|
||||
doAssert t["fruit"]["color"].getInt == 10
|
||||
doAssert t["emails"][0].getStr == "abc"
|
||||
doAssert t["emails"][1].getStr == "123"
|
||||
|
||||
block:
|
||||
# ref objects with cycles.
|
||||
type
|
||||
Misdirection = object
|
||||
cycle: Cycle
|
||||
|
||||
Cycle = ref object
|
||||
foo: string
|
||||
cycle: Misdirection
|
||||
|
||||
let data = """
|
||||
{"cycle": null}
|
||||
"""
|
||||
|
||||
let dataParsed = parseJson(data)
|
||||
let dataDeser = to(dataParsed, Misdirection)
|
||||
|
||||
block:
|
||||
# ref object from #12316
|
||||
type
|
||||
Foo = ref Bar
|
||||
Bar = object
|
||||
|
||||
discard "null".parseJson.to Foo
|
||||
|
||||
block:
|
||||
# named array #12289
|
||||
type Vec = array[2, int]
|
||||
let arr = "[1,2]".parseJson.to Vec
|
||||
doAssert arr == [1,2]
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
discard """
|
||||
errormsg: "The `to` macro does not support ref objects with cycles."
|
||||
file: "tjsonmacro_reject2.nim"
|
||||
line: 10
|
||||
"""
|
||||
import json
|
||||
|
||||
type
|
||||
Misdirection = object
|
||||
cycle: Cycle
|
||||
|
||||
Cycle = ref object
|
||||
foo: string
|
||||
cycle: Misdirection
|
||||
|
||||
let data = """
|
||||
{"cycle": null}
|
||||
"""
|
||||
|
||||
let dataParsed = parseJson(data)
|
||||
let dataDeser = to(dataParsed, Cycle)
|
||||
@@ -334,6 +334,21 @@ block ospaths:
|
||||
doAssert relativePath("/foo", "/fOO", '/') == (when FileSystemCaseSensitive: "../foo" else: "")
|
||||
doAssert relativePath("/foO", "/foo", '/') == (when FileSystemCaseSensitive: "../foO" else: "")
|
||||
|
||||
when doslikeFileSystem:
|
||||
doAssert relativePath(r"c:\foo.nim", r"C:\") == r"foo.nim"
|
||||
doAssert relativePath(r"c:\foo\bar\baz.nim", r"c:\foo") == r"bar\baz.nim"
|
||||
doAssert relativePath(r"c:\foo\bar\baz.nim", r"d:\foo") == r"c:\foo\bar\baz.nim"
|
||||
doAssert relativePath(r"\foo\baz.nim", r"\foo") == r"baz.nim"
|
||||
doAssert relativePath(r"\foo\bar\baz.nim", r"\bar") == r"..\foo\bar\baz.nim"
|
||||
doAssert relativePath(r"\\foo\bar\baz.nim", r"\\foo\bar") == r"baz.nim"
|
||||
doAssert relativePath(r"\\foo\bar\baz.nim", r"\\foO\bar") == r"baz.nim"
|
||||
doAssert relativePath(r"\\foo\bar\baz.nim", r"\\bar\bar") == r"\\foo\bar\baz.nim"
|
||||
doAssert relativePath(r"\\foo\bar\baz.nim", r"\\foo\car") == r"\\foo\bar\baz.nim"
|
||||
doAssert relativePath(r"\\foo\bar\baz.nim", r"\\goo\bar") == r"\\foo\bar\baz.nim"
|
||||
doAssert relativePath(r"\\foo\bar\baz.nim", r"c:\") == r"\\foo\bar\baz.nim"
|
||||
doAssert relativePath(r"\\foo\bar\baz.nim", r"\foo") == r"\\foo\bar\baz.nim"
|
||||
doAssert relativePath(r"c:\foo.nim", r"\foo") == r"c:\foo.nim"
|
||||
|
||||
doAssert joinPath("usr", "") == unixToNativePath"usr/"
|
||||
doAssert joinPath("", "lib") == "lib"
|
||||
doAssert joinPath("", "/lib") == unixToNativePath"/lib"
|
||||
|
||||
@@ -4,7 +4,7 @@ abasdfdsmÄhmaИ
|
||||
Иnastystring
|
||||
A你好
|
||||
ИnastystringA你好
|
||||
ÖÜhmabasdfdsmÄhmaИ'''
|
||||
ÖÜhmabasdfdsmÄhmaИOK'''
|
||||
disabled: "posix"
|
||||
joinable: "false"
|
||||
"""
|
||||
@@ -13,10 +13,10 @@ import winlean
|
||||
|
||||
echo "ÄhmÖÜ"
|
||||
echo "abasdfdsmÄhmaИ"
|
||||
echo "И\0nasty\0\0\0\0string\0"
|
||||
echo "Иnastystring"
|
||||
echo "A你好"
|
||||
|
||||
write stdout, "И\0nasty\0\0\0\0string\0"
|
||||
write stdout, "Иnastystring"
|
||||
writeLine stdout, "A你好"
|
||||
stdout.flushFile()
|
||||
|
||||
@@ -25,3 +25,17 @@ var a = "ÖÜhmabasdfdsmÄhmaИ"
|
||||
var ac = 0'i32
|
||||
discard writeFile(handle, addr a[0], int32(len(a)), addr ac, nil)
|
||||
stdout.flushFile()
|
||||
|
||||
import os
|
||||
|
||||
let str = "some nulls: \0\0\0 (three of them)"
|
||||
|
||||
let fpath = getTempDir() / "file_with_nulls.bin"
|
||||
|
||||
writeFile(fpath, str)
|
||||
|
||||
doAssert(getFileSize(fpath) == 31)
|
||||
doAssert(readFile(fpath) == str)
|
||||
removeFile(fpath)
|
||||
|
||||
echo "OK"
|
||||
|
||||
@@ -22,6 +22,9 @@ outputs = [
|
||||
]
|
||||
|
||||
for i, expected in enumerate(outputs):
|
||||
functionSymbol = gdb.selected_frame().block().function
|
||||
assert functionSymbol.line == 21
|
||||
|
||||
if i == 5:
|
||||
# myArray is passed as pointer to int to myDebug. I look up myArray up in the stack
|
||||
gdb.execute("up")
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
Loading Nim Runtime support.
|
||||
NimEnumPrinter: lookup global symbol 'NTI_z9cu80OJCfNgw9bUdzn5ZEzw_ failed for tyEnum_MyOtherEnum_z9cu80OJCfNgw9bUdzn5ZEzw.
|
||||
NimEnumPrinter: lookup global symbol 'NTI__z9cu80OJCfNgw9bUdzn5ZEzw_ failed for tyEnum_MyOtherEnum__z9cu80OJCfNgw9bUdzn5ZEzw.
|
||||
8
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
discard """
|
||||
output: '''[127, 127, 0, 255]
|
||||
[127, 127, 0, 255]
|
||||
|
||||
(data: 1)
|
||||
'''
|
||||
|
||||
nimout: '''caught Exception
|
||||
@@ -180,3 +182,35 @@ static:
|
||||
var stream = initCtsStream(file)
|
||||
parseAtlas(stream)
|
||||
echo "Done!"
|
||||
|
||||
|
||||
# bug #12244
|
||||
|
||||
type
|
||||
Apple = object
|
||||
data: int
|
||||
|
||||
func what(x: var Apple) =
|
||||
x = Apple(data: 1)
|
||||
|
||||
func oh_no(): Apple =
|
||||
what(result)
|
||||
|
||||
const
|
||||
vmCrash = oh_no()
|
||||
|
||||
debugEcho vmCrash
|
||||
|
||||
|
||||
# bug #12310
|
||||
|
||||
proc someTransform(s: var array[8, uint64]) =
|
||||
var s1 = 5982491417506315008'u64
|
||||
s[1] += s1
|
||||
|
||||
static:
|
||||
var state: array[8, uint64]
|
||||
state[1] = 7105036623409894663'u64
|
||||
someTransform(state)
|
||||
|
||||
doAssert state[1] == 13087528040916209671'u64
|
||||
|
||||
@@ -30,7 +30,7 @@ def getNimRti(type_name):
|
||||
m = type_hash_regex.match(type_name)
|
||||
if m:
|
||||
try:
|
||||
return gdb.parse_and_eval("NTI_" + m.group(1) + "_")
|
||||
return gdb.parse_and_eval("NTI__" + m.group(1) + "_")
|
||||
except:
|
||||
return None
|
||||
|
||||
@@ -340,13 +340,13 @@ def reprEnum(e, typ):
|
||||
return str(e) + " (invalid data!)"
|
||||
|
||||
class NimEnumPrinter:
|
||||
pattern = re.compile(r'^tyEnum_(\w*)_([A-Za-z0-9]*)$')
|
||||
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) + "_"
|
||||
typeInfoName = "NTI__" + match.group(2) + "_"
|
||||
self.nti = gdb.lookup_global_symbol(typeInfoName)
|
||||
|
||||
if self.nti is None:
|
||||
@@ -373,7 +373,7 @@ class NimSetPrinter:
|
||||
match = self.pattern.match(self.val.type.name)
|
||||
self.typeNimName = match.group(1)
|
||||
|
||||
typeInfoName = "NTI_" + match.group(2) + "_"
|
||||
typeInfoName = "NTI__" + match.group(2) + "_"
|
||||
self.nti = gdb.lookup_global_symbol(typeInfoName)
|
||||
|
||||
if self.nti is None:
|
||||
|
||||
Reference in New Issue
Block a user