Merge branch 'devel' into IOSelector_unregister_fix

This commit is contained in:
Dominik Picheta 2019-10-22 11:31:38 -07:00 • committed by GitHub
commit 92fa7e0579
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
94 changed files with 1729 additions and 1223 deletions

16
build_all.bat Normal file
View 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

View file

@ -7,6 +7,7 @@
### Breaking changes in the standard library ### Breaking changes in the standard library
- `base64.encode` no longer supports `lineLen` and `newLine` use `base64.encodeMIME` instead.
### Breaking changes in the compiler ### 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 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. - `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 ## Language additions
@ -31,6 +34,7 @@
## Language changes ## Language changes
- Unsigned integer operators have been fixed to allow promotion of the first operand.
### Tool changes ### Tool changes

View file

@ -1810,11 +1810,11 @@ template getBody*(s: PSym): PNode = s.ast[bodyPos]
template detailedInfo*(sym: PSym): string = template detailedInfo*(sym: PSym): string =
sym.name.s sym.name.s
proc isInlineIterator*(s: PSym): bool {.inline.} = proc isInlineIterator*(typ: PType): bool {.inline.} =
s.kind == skIterator and s.typ.callConv != ccClosure typ.kind == tyProc and tfIterator in typ.flags and typ.callConv != ccClosure
proc isClosureIterator*(s: PSym): bool {.inline.} = proc isClosureIterator*(typ: PType): bool {.inline.} =
s.kind == skIterator and s.typ.callConv == ccClosure typ.kind == tyProc and tfIterator in typ.flags and typ.callConv == ccClosure
proc isSinkParam*(s: PSym): bool {.inline.} = proc isSinkParam*(s: PSym): bool {.inline.} =
s.kind == skParam and (s.typ.kind == tySink or tfHasOwned in s.typ.flags) s.kind == skParam and (s.typ.kind == tySink or tfHasOwned in s.typ.flags)

View file

@ -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 # with the new semantics for 'nil' strings, we can map "" to nil and
# save tons of allocations: # save tons of allocations:
if n.strVal.len == 0 and optNilSeqs notin p.options and 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) result = genNilStringLiteral(p.module, n.info)
else: else:
result = genStringLiteral(p.module, n) 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 # 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 # here for this flag, where it is reasonably safe to do so
# (for objects, etc.): # (for objects, etc.):
if p.config.selectedGC == gcDestructors: if optSeqDestructors in p.config.globalOptions:
linefmt(p, cpsStmts, linefmt(p, cpsStmts,
"$1 = $2;$n", "$1 = $2;$n",
[rdLoc(dest), rdLoc(src)]) [rdLoc(dest), rdLoc(src)])
@ -279,7 +279,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
of tyRef: of tyRef:
genRefAssign(p, dest, src) genRefAssign(p, dest, src)
of tySequence: of tySequence:
if p.config.selectedGC == gcDestructors: if optSeqDestructors in p.config.globalOptions:
genGenericAsgn(p, dest, src, flags) genGenericAsgn(p, dest, src, flags)
elif (needToCopy notin flags and src.storage != OnStatic) or canMove(p, src.lode, dest): elif (needToCopy notin flags and src.storage != OnStatic) or canMove(p, src.lode, dest):
genRefAssign(p, dest, src) genRefAssign(p, dest, src)
@ -288,7 +288,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
[addrLoc(p.config, dest), rdLoc(src), [addrLoc(p.config, dest), rdLoc(src),
genTypeInfo(p.module, dest.t, dest.lode.info)]) genTypeInfo(p.module, dest.t, dest.lode.info)])
of tyString: of tyString:
if p.config.selectedGC == gcDestructors: if optSeqDestructors in p.config.globalOptions:
genGenericAsgn(p, dest, src, flags) genGenericAsgn(p, dest, src, flags)
elif (needToCopy notin flags and src.storage != OnStatic) or canMove(p, src.lode, dest): elif (needToCopy notin flags and src.storage != OnStatic) or canMove(p, src.lode, dest):
genRefAssign(p, dest, src) 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]) a.r = ropecg(p.module, "(*$1)", [a.r])
if lfPrepareForMutation in d.flags and ty.kind == tyString and 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)]) linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)])
putIntoDest(p, d, n, putIntoDest(p, d, n,
ropecg(p.module, "$1$3[$2]", [rdLoc(a), rdCharLoc(b), dataField(p)]), a.storage) 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) if conf.selectedGC == gcNone: message(conf, n.info, warnGcMem, n.renderTree)
proc strLoc(p: BProc; d: TLoc): Rope = proc strLoc(p: BProc; d: TLoc): Rope =
if p.config.selectedGC == gcDestructors: if optSeqDestructors in p.config.globalOptions:
result = byRefLoc(p, d) result = byRefLoc(p, d)
else: else:
result = rdLoc(d) result = rdLoc(d)
@ -1141,7 +1141,7 @@ proc genStrAppend(p: BProc, e: PNode, d: var TLoc) =
add(lens, " + ") add(lens, " + ")
add(appends, ropecg(p.module, "#appendString($1, $2);$n", add(appends, ropecg(p.module, "#appendString($1, $2);$n",
[strLoc(p, dest), rdLoc(a)])) [strLoc(p, dest), rdLoc(a)]))
if p.config.selectedGC == gcDestructors: if optSeqDestructors in p.config.globalOptions:
linefmt(p, cpsStmts, "#prepareAdd($1, $2$3);$n", linefmt(p, cpsStmts, "#prepareAdd($1, $2$3);$n",
[byRefLoc(p, dest), lens, L]) [byRefLoc(p, dest), lens, L])
else: else:
@ -1206,7 +1206,7 @@ proc rawGenNew(p: BProc, a: TLoc, sizeExpr: Rope) =
if sizeExpr.isNil: if sizeExpr.isNil:
sizeExpr = "sizeof($1)" % [getTypeDesc(p.module, bt)] 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)", b.r = ropecg(p.module, "($1) #nimNewObj($2)",
[getTypeDesc(p.module, typ), sizeExpr]) [getTypeDesc(p.module, typ), sizeExpr])
genAssignment(p, a, b, {}) genAssignment(p, a, b, {})
@ -1288,7 +1288,7 @@ proc genNewSeq(p: BProc, e: PNode) =
var a, b: TLoc var a, b: TLoc
initLocExpr(p, e.sons[1], a) initLocExpr(p, e.sons[1], a)
initLocExpr(p, e.sons[2], b) initLocExpr(p, e.sons[2], b)
if p.config.selectedGC == gcDestructors: if optSeqDestructors in p.config.globalOptions:
let seqtype = skipTypes(e.sons[1].typ, abstractVarRange) let seqtype = skipTypes(e.sons[1].typ, abstractVarRange)
linefmt(p, cpsStmts, "$1.len = $2; $1.p = ($4*) #newSeqPayload($2, sizeof($3));$n", linefmt(p, cpsStmts, "$1.len = $2; $1.p = ($4*) #newSeqPayload($2, sizeof($3));$n",
[a.rdLoc, b.rdLoc, getTypeDesc(p.module, seqtype.lastSon), [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) let seqtype = skipTypes(e.typ, abstractVarRange)
var a: TLoc var a: TLoc
initLocExpr(p, e.sons[1], a) 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) 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", linefmt(p, cpsStmts, "$1.len = 0; $1.p = ($4*) #newSeqPayload($2, sizeof($3));$n",
[d.rdLoc, a.rdLoc, getTypeDesc(p.module, seqtype.lastSon), [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) getTemp(p, n.typ, d)
let l = intLiteral(len(n)) let l = intLiteral(len(n))
if p.config.selectedGC == gcDestructors: if optSeqDestructors in p.config.globalOptions:
let seqtype = n.typ let seqtype = n.typ
linefmt(p, cpsStmts, "$1.len = $2; $1.p = ($4*) #newSeqPayload($2, sizeof($3));$n", linefmt(p, cpsStmts, "$1.len = $2; $1.p = ($4*) #newSeqPayload($2, sizeof($3));$n",
[rdLoc dest[], l, getTypeDesc(p.module, seqtype.lastSon), [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) getTemp(p, n.typ, d)
# generate call to newSeq before adding the elements per hand: # generate call to newSeq before adding the elements per hand:
let L = toInt(lengthOrd(p.config, n.sons[1].typ)) 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 let seqtype = n.typ
linefmt(p, cpsStmts, "$1.len = $2; $1.p = ($4*) #newSeqPayload($2, sizeof($3));$n", linefmt(p, cpsStmts, "$1.len = $2; $1.p = ($4*) #newSeqPayload($2, sizeof($3));$n",
[rdLoc d, L, getTypeDesc(p.module, seqtype.lastSon), [rdLoc d, L, getTypeDesc(p.module, seqtype.lastSon),
@ -1484,7 +1484,7 @@ proc genNewFinalize(p: BProc, e: PNode) =
gcUsage(p.config, e) gcUsage(p.config, e)
proc genOfHelper(p: BProc; dest: PType; a: Rope; info: TLineInfo): Rope = 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)", result = ropecg(p.module, "#isObj($1.m_type, $2)",
[a, genTypeInfo2Name(p.module, dest)]) [a, genTypeInfo2Name(p.module, dest)])
else: else:
@ -1535,7 +1535,7 @@ proc genOf(p: BProc, n: PNode, d: var TLoc) =
genOf(p, n.sons[1], n.sons[2].typ, d) genOf(p, n.sons[1], n.sons[2].typ, d)
proc genRepr(p: BProc, e: PNode, d: var TLoc) = 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") localError(p.config, e.info, "'repr' is not available for --newruntime")
var a: TLoc var a: TLoc
initLocExpr(p, e.sons[1], a) initLocExpr(p, e.sons[1], a)
@ -1654,7 +1654,7 @@ proc makeAddr(n: PNode): PNode =
result.typ = makePtrType(n.typ) result.typ = makePtrType(n.typ)
proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc) = 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]) e.sons[1] = makeAddr(e[1])
genCall(p, e, d) genCall(p, e, d)
return return
@ -1683,7 +1683,7 @@ proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc) =
gcUsage(p.config, e) gcUsage(p.config, e)
proc genSetLengthStr(p: BProc, e: PNode, d: var TLoc) = 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") binaryStmtAddr(p, e, d, "setLengthStrV2")
else: else:
var a, b, call: TLoc 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: if srcTyp.kind in {tyPtr, tyPointer} and etyp.kind in IntegralTypes:
putIntoDest(p, d, e, "(($1) (ptrdiff_t) ($2))" % putIntoDest(p, d, e, "(($1) (ptrdiff_t) ($2))" %
[getTypeDesc(p.module, e.typ), rdCharLoc(a)], a.storage) [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))" % putIntoDest(p, d, e, "(*($1*) (&$2))" %
[getTypeDesc(p.module, e.typ), rdCharLoc(a)], a.storage) [getTypeDesc(p.module, e.typ), rdCharLoc(a)], a.storage)
else: else:
@ -2025,7 +2025,7 @@ proc genMove(p: BProc; n: PNode; d: var TLoc) =
resetLoc(p, a) resetLoc(p, a)
proc genDestroy(p: BProc; n: PNode) = proc genDestroy(p: BProc; n: PNode) =
if p.config.selectedGC == gcDestructors: if optSeqDestructors in p.config.globalOptions:
let arg = n[1].skipAddr let arg = n[1].skipAddr
let t = arg.typ.skipTypes(abstractInst) let t = arg.typ.skipTypes(abstractInst)
case t.kind 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 mConStrStr: genStrConcat(p, e, d)
of mAppendStrCh: of mAppendStrCh:
if p.config.selectedGC == gcDestructors: if optSeqDestructors in p.config.globalOptions:
binaryStmtAddr(p, e, d, "nimAddCharV1") binaryStmtAddr(p, e, d, "nimAddCharV1")
else: else:
var dest, b, call: TLoc var dest, b, call: TLoc
@ -2131,7 +2131,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
genAssignment(p, dest, call, {}) genAssignment(p, dest, call, {})
of mAppendStrStr: genStrAppend(p, e, d) of mAppendStrStr: genStrAppend(p, e, d)
of mAppendSeqElem: of mAppendSeqElem:
if p.config.selectedGC == gcDestructors: if optSeqDestructors in p.config.globalOptions:
e.sons[1] = makeAddr(e[1]) e.sons[1] = makeAddr(e[1])
genCall(p, e, d) genCall(p, e, d)
else: 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 mCStrToStr: genDollar(p, e, d, "#cstrToNimstr($1)")
of mStrToStr, mUnown: expr(p, e.sons[1], d) of mStrToStr, mUnown: expr(p, e.sons[1], d)
of mEnumToStr: of mEnumToStr:
if optNimV2 in p.config.globalOptions: if optTinyRtti in p.config.globalOptions:
genEnumToStr(p, e, d) genEnumToStr(p, e, d)
else: else:
genRepr(p, e, d) 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: while t.kind == tyObject and t.sons[0] != nil:
add(r, ".Sup") add(r, ".Sup")
t = skipTypes(t.sons[0], skipPtrs) 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) genTypeInfo2Name(p.module, dest)
else: else:
genTypeInfo(p.module, dest, n.info) genTypeInfo(p.module, dest, n.info)
@ -2713,7 +2713,7 @@ proc getDefaultValue(p: BProc; typ: PType; info: TLineInfo): Rope =
tyTyped, tyTypeDesc, tyStatic, tyRef, tyNil: tyTyped, tyTypeDesc, tyStatic, tyRef, tyNil:
result = rope"NIM_NIL" result = rope"NIM_NIL"
of tyString, tySequence: of tyString, tySequence:
if p.config.selectedGC == gcDestructors: if optSeqDestructors in p.config.globalOptions:
result = rope"{0, NIM_NIL}" result = rope"{0, NIM_NIL}"
else: else:
result = rope"NIM_NIL" result = rope"NIM_NIL"
@ -2855,7 +2855,7 @@ proc genConstExpr(p: BProc, n: PNode): Rope =
of nkBracket, nkPar, nkTupleConstr, nkClosure: of nkBracket, nkPar, nkTupleConstr, nkClosure:
var t = skipTypes(n.typ, abstractInstOwned) var t = skipTypes(n.typ, abstractInstOwned)
if t.kind == tySequence: if t.kind == tySequence:
if p.config.selectedGC == gcDestructors: if optSeqDestructors in p.config.globalOptions:
result = genConstSeqV2(p, n, n.typ) result = genConstSeqV2(p, n, n.typ)
else: else:
result = genConstSeq(p, n, n.typ) result = genConstSeq(p, n, n.typ)
@ -2879,7 +2879,7 @@ proc genConstExpr(p: BProc, n: PNode): Rope =
of nkObjConstr: of nkObjConstr:
result = genConstObjConstr(p, n) result = genConstObjConstr(p, n)
of nkStrLit..nkTripleStrLit: of nkStrLit..nkTripleStrLit:
if p.config.selectedGC == gcDestructors: if optSeqDestructors in p.config.globalOptions:
result = genStringLiteralV2Const(p.module, n) result = genStringLiteralV2Const(p.module, n)
else: else:
var d: TLoc var d: TLoc

View file

@ -17,7 +17,7 @@ const
proc getTraverseProc(p: BProc, v: PSym): Rope = proc getTraverseProc(p: BProc, v: PSym): Rope =
if p.config.selectedGC in {gcMarkAndSweep, gcDestructors, gcV2, gcRefc} and 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): containsGarbageCollectedRef(v.loc.t):
# we register a specialized marked proc here; this has the advantage # we register a specialized marked proc here; this has the advantage
# that it works out of the box for thread local storage then :-) # 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), [e, makeCString(typ.sym.name.s),
makeCString(if p.prc != nil: p.prc.name.s else: p.module.module.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)]) 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]) lineCg(p, cpsStmts, "$1 = NIM_NIL;$n", [e])
else: else:
genLineDir(p, t) genLineDir(p, t)
@ -1057,7 +1057,7 @@ proc genTry(p: BProc, t: PNode, d: var TLoc) =
for j in 0 .. blen - 2: for j in 0 .. blen - 2:
assert(t.sons[i].sons[j].kind == nkType) assert(t.sons[i].sons[j].kind == nkType)
if orExpr != nil: add(orExpr, "||") 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) genTypeInfo2Name(p.module, t[i][j].typ)
else: else:
genTypeInfo(p.module, t[i][j].typ, t[i][j].info) 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) getTemp(p, a.t, tmp)
expr(p, e.sons[1], tmp) expr(p, e.sons[1], tmp)
let field = dotExpr.sons[1].sym 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) let t = dotExpr[0].typ.skipTypes(abstractInst)
var oldVal, newVal: TLoc var oldVal, newVal: TLoc
genCaseObjDiscMapping(p, e[0], t, field, oldVal) genCaseObjDiscMapping(p, e[0], t, field, oldVal)

View file

@ -17,7 +17,7 @@ type
visitorFrmt: string visitorFrmt: string
const const
visitorFrmt = "#nimGCvisit((void*)$1, $2);$n" visitorFrmt = "#nimGCvisit((void*)$1, $2);$n"
proc genTraverseProc(c: TTraversalClosure, accessor: Rope, typ: PType) proc genTraverseProc(c: TTraversalClosure, accessor: Rope, typ: PType)
proc genCaseRange(p: BProc, branch: PNode) proc genCaseRange(p: BProc, branch: PNode)
@ -104,7 +104,8 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, typ: PType) =
elif containsGarbageCollectedRef(typ.lastSon): elif containsGarbageCollectedRef(typ.lastSon):
# destructor based seqs are themselves not traced but their data is, if # destructor based seqs are themselves not traced but their data is, if
# they contain a GC'ed type: # 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: of tyString:
if tfHasAsgn notin typ.flags: if tfHasAsgn notin typ.flags:
lineCg(p, cpsStmts, visitorFrmt, [accessor, c.visitorFrmt]) lineCg(p, cpsStmts, visitorFrmt, [accessor, c.visitorFrmt])

View file

@ -356,7 +356,7 @@ proc addForwardStructFormat(m: BModule, structOrUnion: Rope, typename: Rope) =
m.s[cfsForwardTypes].addf "typedef $1 $2 $2;$n", [structOrUnion, typename] m.s[cfsForwardTypes].addf "typedef $1 $2 $2;$n", [structOrUnion, typename]
proc seqStar(m: BModule): string = proc seqStar(m: BModule): string =
if m.config.selectedGC == gcDestructors: result = "" if optSeqDestructors in m.config.globalOptions: result = ""
else: result = "*" else: result = "*"
proc getTypeForward(m: BModule, typ: PType; sig: SigHash): Rope = 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) pushType(m, t)
of tySequence: of tySequence:
let sig = hashType(t) let sig = hashType(t)
if m.config.selectedGC == gcDestructors: if optSeqDestructors in m.config.globalOptions:
if skipTypes(etB.sons[0], typedescInst).kind == tyEmpty: if skipTypes(etB.sons[0], typedescInst).kind == tyEmpty:
internalError(m.config, "cannot map the empty seq type to a C type") 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 result = name & star
m.typeCache[sig] = result m.typeCache[sig] = result
of tySequence: of tySequence:
if m.config.selectedGC == gcDestructors: if optSeqDestructors in m.config.globalOptions:
result = getTypeDescWeak(m, et, check) & star result = getTypeDescWeak(m, et, check) & star
m.typeCache[sig] = result m.typeCache[sig] = result
else: else:
@ -770,7 +770,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
"void* ClE_0;$n} $1;$n", "void* ClE_0;$n} $1;$n",
[result, rettype, desc]) [result, rettype, desc])
of tySequence: of tySequence:
if m.config.selectedGC == gcDestructors: if optSeqDestructors in m.config.globalOptions:
result = getTypeDescWeak(m, t, check) result = getTypeDescWeak(m, t, check)
else: else:
# we cannot use getTypeForward here because then t would be associated # we cannot use getTypeForward here because then t would be associated
@ -926,7 +926,7 @@ proc finishTypeDescriptions(m: BModule) =
var check = initIntSet() var check = initIntSet()
while i < len(m.typeStack): while i < len(m.typeStack):
let t = m.typeStack[i] 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) seqV2ContentType(m, t, check)
else: else:
discard getTypeDescAux(m, t, check) discard getTypeDescAux(m, t, check)
@ -1351,7 +1351,7 @@ proc genTypeInfo(m: BModule, t: PType; info: TLineInfo): Rope =
genTupleInfo(m, x, x, result, info) genTupleInfo(m, x, x, result, info)
of tySequence: of tySequence:
genTypeInfoAux(m, t, t, result, info) genTypeInfoAux(m, t, t, result, info)
if m.config.selectedGC != gcDestructors: if optSeqDestructors notin m.config.globalOptions:
if m.config.selectedGC >= gcMarkAndSweep: if m.config.selectedGC >= gcMarkAndSweep:
let markerProc = genTraverseProc(m, origType, sig) let markerProc = genTraverseProc(m, origType, sig)
addf(m.s[cfsTypeInit3], "$1.marker = $2;$n", [tiNameForHcr(m, result), markerProc]) 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 tySet: genSetInfo(m, t, result, info)
of tyEnum: genEnumInfo(m, t, result, info) of tyEnum: genEnumInfo(m, t, result, info)
of tyObject: of tyObject:
if optNimV2 in m.config.globalOptions: if optTinyRtti in m.config.globalOptions:
genObjectInfoV2(m, t, origType, result, info) genObjectInfoV2(m, t, origType, result, info)
else: else:
genObjectInfo(m, t, origType, result, info) genObjectInfo(m, t, origType, result, info)

View file

@ -293,13 +293,13 @@ proc lenField(p: BProc): Rope =
result = rope(if p.module.compileToCpp: "len" else: "Sup.len") result = rope(if p.module.compileToCpp: "len" else: "Sup.len")
proc lenExpr(p: BProc; a: TLoc): Rope = proc lenExpr(p: BProc; a: TLoc): Rope =
if p.config.selectedGC == gcDestructors: if optSeqDestructors in p.config.globalOptions:
result = rdLoc(a) & ".len" result = rdLoc(a) & ".len"
else: else:
result = "($1 ? $1->$2 : 0)" % [rdLoc(a), lenField(p)] result = "($1 ? $1->$2 : 0)" % [rdLoc(a), lenField(p)]
proc dataField(p: BProc): Rope = proc dataField(p: BProc): Rope =
if p.config.selectedGC == gcDestructors: if optSeqDestructors in p.config.globalOptions:
result = rope".p->data" result = rope".p->data"
else: else:
result = rope"->data" result = rope"->data"
@ -347,7 +347,7 @@ proc genObjectInit(p: BProc, section: TCProcSection, t: PType, a: TLoc,
s = skipTypes(s.sons[0], skipPtrs) s = skipTypes(s.sons[0], skipPtrs)
linefmt(p, section, "$1.m_type = $2;$n", [r, genTypeInfo(p.module, t, a.lode.info)]) linefmt(p, section, "$1.m_type = $2;$n", [r, genTypeInfo(p.module, t, a.lode.info)])
of frEmbedded: of frEmbedded:
if optNimV2 in p.config.globalOptions: if optTinyRtti in p.config.globalOptions:
localError(p.config, p.prc.info, localError(p.config, p.prc.info,
"complex object initialization is not supported with --newruntime") "complex object initialization is not supported with --newruntime")
# worst case for performance: # worst case for performance:
@ -377,10 +377,10 @@ proc isComplexValueType(t: PType): bool {.inline.} =
(t.kind == tyProc and t.callConv == ccClosure) (t.kind == tyProc and t.callConv == ccClosure)
proc resetLoc(p: BProc, loc: var TLoc) = 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) let typ = skipTypes(loc.t, abstractVarRange)
if isImportedCppType(typ): return 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 assert rdLoc(loc) != nil
linefmt(p, cpsStmts, "$1.len = 0; $1.p = NIM_NIL;$n", [rdLoc(loc)]) linefmt(p, cpsStmts, "$1.len = 0; $1.p = NIM_NIL;$n", [rdLoc(loc)])
elif not isComplexValueType(typ): elif not isComplexValueType(typ):
@ -411,7 +411,7 @@ proc resetLoc(p: BProc, loc: var TLoc) =
proc constructLoc(p: BProc, loc: TLoc, isTemp = false) = proc constructLoc(p: BProc, loc: TLoc, isTemp = false) =
let typ = loc.t 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)]) linefmt(p, cpsStmts, "$1.len = 0; $1.p = NIM_NIL;$n", [rdLoc(loc)])
elif not isComplexValueType(typ): elif not isComplexValueType(typ):
linefmt(p, cpsStmts, "$1 = ($2)0;$n", [rdLoc(loc), linefmt(p, cpsStmts, "$1 = ($2)0;$n", [rdLoc(loc),
@ -997,13 +997,14 @@ proc genProcAux(m: BModule, prc: PSym) =
closureSetup(p, prc) closureSetup(p, prc)
genStmts(p, procBody) # modifies p.locals, p.init, etc. genStmts(p, procBody) # modifies p.locals, p.init, etc.
var generatedProc: Rope var generatedProc: Rope
generatedProc.genCLineDir prc.info, m.config
if sfNoReturn in prc.flags: if sfNoReturn in prc.flags:
if hasDeclspec in extccomp.CC[p.config.cCompiler].props: if hasDeclspec in extccomp.CC[p.config.cCompiler].props:
header = "__declspec(noreturn) " & header header = "__declspec(noreturn) " & header
if sfPure in prc.flags: if sfPure in prc.flags:
if hasDeclspec in extccomp.CC[p.config.cCompiler].props: if hasDeclspec in extccomp.CC[p.config.cCompiler].props:
header = "__declspec(naked) " & header 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)]) [header, p.s(cpsLocals), p.s(cpsInit), p.s(cpsStmts)])
else: else:
if m.hcrOn and isReloadable(m, prc): 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 # 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 # call each other using directly the "_actual" versions (an optimization) - see issue #11608
addf(m.s[cfsProcHeaders], "$1;\n", [header]) 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)) add(generatedProc, initGCFrame(p))
if optStackTrace in prc.options: if optStackTrace in prc.options:
add(generatedProc, p.s(cpsLocals)) add(generatedProc, p.s(cpsLocals))

View file

@ -683,7 +683,8 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
n[0] = ex n[0] = ex
result.add(n) result.add(n)
of nkCast, nkHiddenStdConv, nkHiddenSubConv, nkConv, nkObjDownConv: of nkCast, nkHiddenStdConv, nkHiddenSubConv, nkConv, nkObjDownConv,
nkDerefExpr, nkHiddenDeref:
var ns = false var ns = false
for i in 0 ..< n.len: for i in 0 ..< n.len:
n[i] = ctx.lowerStmtListExprs(n[i], ns) 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[0] = newSymNode(ctx.g.getSysSym(n[0].info, "true"))
n[1] = newBody n[1] = newBody
of nkDotExpr: of nkDotExpr, nkCheckedFieldExpr:
var ns = false var ns = false
n[0] = ctx.lowerStmtListExprs(n[0], ns) n[0] = ctx.lowerStmtListExprs(n[0], ns)
if ns: if ns:

View file

@ -452,6 +452,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
of "destructors": of "destructors":
conf.selectedGC = gcDestructors conf.selectedGC = gcDestructors
defineSymbol(conf.symbols, "gcdestructors") defineSymbol(conf.symbols, "gcdestructors")
incl conf.globalOptions, optSeqDestructors
of "go": of "go":
conf.selectedGC = gcGo conf.selectedGC = gcGo
defineSymbol(conf.symbols, "gogc") defineSymbol(conf.symbols, "gogc")
@ -758,10 +759,17 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
if pass in {passCmd2, passPP}: if pass in {passCmd2, passPP}:
doAssert(conf != nil) doAssert(conf != nil)
incl(conf.features, destructor) incl(conf.features, destructor)
incl(conf.globalOptions, optNimV2) incl(conf.globalOptions, optTinyRtti)
incl(conf.globalOptions, optOwnedRefs)
incl(conf.globalOptions, optSeqDestructors)
defineSymbol(conf.symbols, "nimV2") defineSymbol(conf.symbols, "nimV2")
conf.selectedGC = gcDestructors conf.selectedGC = gcDestructors
defineSymbol(conf.symbols, "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": of "stylecheck":
case arg.normalize case arg.normalize
of "off": conf.globalOptions = conf.globalOptions - {optStyleHint, optStyleError} of "off": conf.globalOptions = conf.globalOptions - {optStyleHint, optStyleError}

View file

@ -53,7 +53,6 @@ proc initDefines*(symbols: StringTableRef) =
defineSymbol("nimKnowsNimvm") defineSymbol("nimKnowsNimvm")
defineSymbol("nimArrIdx") defineSymbol("nimArrIdx")
defineSymbol("nimHasalignOf") defineSymbol("nimHasalignOf")
defineSymbol("nimNewShiftOps")
defineSymbol("nimDistros") defineSymbol("nimDistros")
defineSymbol("nimHasCppDefine") defineSymbol("nimHasCppDefine")
defineSymbol("nimGenericInOutFlags") defineSymbol("nimGenericInOutFlags")
@ -99,3 +98,4 @@ proc initDefines*(symbols: StringTableRef) =
defineSymbol("nimHasStyleChecks") defineSymbol("nimHasStyleChecks")
defineSymbol("nimToOpenArrayCString") defineSymbol("nimToOpenArrayCString")
defineSymbol("nimHasUsed") defineSymbol("nimHasUsed")
defineSymbol("nimFixedForwardGeneric")

View file

@ -63,8 +63,8 @@ compiler gcc:
result = ( result = (
name: "gcc", name: "gcc",
objExt: "o", objExt: "o",
optSpeed: " -O3 ", optSpeed: " -O3 -fno-ident",
optSize: " -Os ", optSize: " -Os -fno-ident",
compilerExe: "gcc", compilerExe: "gcc",
cppCompiler: "g++", cppCompiler: "g++",
compileTmpl: "-c $options $include -o $objfile $file", 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 if optForceFullMake notin conf.globalOptions and fileExists(c.obj) and
not externalFileChanged(conf, c): not externalFileChanged(conf, c):
c.flags.incl CfileFlag.Cached 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) conf.toCompile.add(c)
proc addExternalFileToCompile*(conf: ConfigRef; filename: AbsoluteFile) = proc addExternalFileToCompile*(conf: ConfigRef; filename: AbsoluteFile) =

View file

@ -436,7 +436,7 @@ proc sameTree*(a, b: PNode): bool =
if not result and a.sym.magic != mNone: if not result and a.sym.magic != mNone:
result = a.sym.magic == b.sym.magic or sameOpr(a.sym, b.sym) result = a.sym.magic == b.sym.magic or sameOpr(a.sym, b.sym)
of nkIdent: result = a.ident.id == b.ident.id 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 nkFloatLit..nkFloat64Lit: result = a.floatVal == b.floatVal
of nkStrLit..nkTripleStrLit: result = a.strVal == b.strVal of nkStrLit..nkTripleStrLit: result = a.strVal == b.strVal
of nkType: result = a.typ == b.typ of nkType: result = a.typ == b.typ

View file

@ -486,7 +486,7 @@ proc p(n: PNode; c: var Con): PNode =
result.add n[0] result.add n[0]
result.add p(n[1], c) result.add p(n[1], c)
of nkRaiseStmt: 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: if n[0].kind in nkCallKinds:
let call = p(n[0], c) let call = p(n[0], c)
result = copyNode(n) result = copyNode(n)
@ -636,7 +636,7 @@ proc reverseDestroys(destroys: seq[PNode]): seq[PNode] =
result.add destroys[i] result.add destroys[i]
proc injectDestructorCalls*(g: ModuleGraph; owner: PSym; n: PNode): PNode = 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 var c: Con
c.owner = owner c.owner = owner
c.destroys = newNodeI(nkStmtList, n.info) c.destroys = newNodeI(nkStmtList, n.info)

View file

@ -436,7 +436,7 @@ proc divMod*(dividend, divisor: Int128): tuple[quotient, remainder: Int128] =
result.quotient = -quotient result.quotient = -quotient
else: else:
result.quotient = quotient result.quotient = quotient
if isNegativeB: if isNegativeA:
result.remainder = -dividend result.remainder = -dividend
else: else:
result.remainder = dividend result.remainder = dividend
@ -676,3 +676,12 @@ when isMainModule:
doAssert $high(Int128) == "170141183460469231731687303715884105727" doAssert $high(Int128) == "170141183460469231731687303715884105727"
doAssert $low(Int128) == "-170141183460469231731687303715884105728" 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))

View file

@ -257,7 +257,7 @@ proc liftIterSym*(g: ModuleGraph; n: PNode; owner: PSym): PNode =
result.add(v) result.add(v)
# add 'new' statement: # add 'new' statement:
result.add newCall(getSysSym(g, n.info, "internalNew"), env) 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) createTypeBoundOps(g, nil, env.typ, n.info)
result.add makeClosure(g, iter, env, 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 c.ownerToType[owner.id] = result
proc asOwnedRef(c: DetectionPass; t: PType): PType = 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 assert t.kind == tyRef
result = newType(tyOwned, t.owner) result = newType(tyOwned, t.owner)
result.flags.incl tfHasOwned result.flags.incl tfHasOwned
@ -342,9 +342,9 @@ proc createUpField(c: var DetectionPass; dest, dep: PSym; info: TLineInfo) =
let obj = refObj.skipTypes({tyOwned, tyRef}) let obj = refObj.skipTypes({tyOwned, tyRef})
# The assumption here is that gcDestructors means we cannot deal # The assumption here is that gcDestructors means we cannot deal
# with cycles properly, so it's better to produce a weak ref (=ptr) here. # 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 # This seems to be generally correct but since it's a bit risky it's disabled
# enabled for gcDestructors. # for now.
let fieldType = if false: # c.graph.config.selectedGC == gcDestructors: let fieldType = if false: # optSeqDestructors in c.graph.config.globalOptions:
c.getEnvTypeForOwnerUp(dep, info) #getHiddenParam(dep).typ c.getEnvTypeForOwnerUp(dep, info) #getHiddenParam(dep).typ
else: else:
c.getEnvTypeForOwner(dep, info) c.getEnvTypeForOwner(dep, info)
@ -477,7 +477,7 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
w = up w = up
of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit, of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit,
nkTemplateDef, nkTypeSection, nkProcDef, nkMethodDef, nkTemplateDef, nkTypeSection, nkProcDef, nkMethodDef,
nkConverterDef, nkMacroDef, nkFuncDef, nkCommentStmt: nkConverterDef, nkMacroDef, nkFuncDef, nkCommentStmt, nkTypeOfExpr:
discard discard
of nkLambdaKinds, nkIteratorDef: of nkLambdaKinds, nkIteratorDef:
if n.typ != nil: 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" localError d.graph.config, owner.info, "internal error: could not determine closure type"
result = newEnvVar(d.graph.cache, owner, asOwnedRef(d, envVarType), info) result = newEnvVar(d.graph.cache, owner, asOwnedRef(d, envVarType), info)
c.envVars[owner.id] = result 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) var v = newSym(skVar, getIdent(d.graph.cache, envName & "Alt"), owner, info)
v.flags = {sfShadowed, sfGeneratedOp} v.flags = {sfShadowed, sfGeneratedOp}
v.typ = envVarType v.typ = envVarType
@ -572,14 +572,14 @@ proc rawClosureCreation(owner: PSym;
var v = newNodeI(nkVarSection, env.info) var v = newNodeI(nkVarSection, env.info)
addVar(v, env) addVar(v, env)
result.add(v) result.add(v)
if optNimV2 in d.graph.config.globalOptions: if optOwnedRefs in d.graph.config.globalOptions:
let unowned = c.unownedEnvVars[owner.id] let unowned = c.unownedEnvVars[owner.id]
assert unowned != nil assert unowned != nil
addVar(v, unowned) addVar(v, unowned)
# add 'new' statement: # add 'new' statement:
result.add(newCall(getSysSym(d.graph, env.info, "internalNew"), env)) 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] let unowned = c.unownedEnvVars[owner.id]
assert unowned != nil assert unowned != nil
let env2 = copyTree(env) let env2 = copyTree(env)
@ -608,12 +608,12 @@ proc rawClosureCreation(owner: PSym;
localError(d.graph.config, env.info, "internal error: cannot create up reference") 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 # we are not in the sem'check phase anymore! so pass 'nil' for the PContext
# and hope for the best: # 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) createTypeBoundOps(d.graph, nil, env.typ, owner.info)
proc finishClosureCreation(owner: PSym; d: DetectionPass; c: LiftingPass; proc finishClosureCreation(owner: PSym; d: DetectionPass; c: LiftingPass;
info: TLineInfo; res: PNode) = info: TLineInfo; res: PNode) =
if optNimV2 in d.graph.config.globalOptions: if optOwnedRefs in d.graph.config.globalOptions:
let unowned = c.unownedEnvVars[owner.id] let unowned = c.unownedEnvVars[owner.id]
assert unowned != nil assert unowned != nil
let nilLit = newNodeIT(nkNilLit, info, unowned.typ) let nilLit = newNodeIT(nkNilLit, info, unowned.typ)
@ -637,7 +637,7 @@ proc closureCreationForIter(iter: PNode;
addVar(vs, vnode) addVar(vs, vnode)
result.add(vs) result.add(vs)
result.add(newCall(getSysSym(d.graph, iter.info, "internalNew"), vnode)) 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) createTypeBoundOps(d.graph, nil, vnode.typ, iter.info)
let upField = lookupInRecord(v.typ.skipTypes({tyOwned, tyRef}).n, getIdent(d.graph.cache, upName)) 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; proc accessViaEnvVar(n: PNode; owner: PSym; d: DetectionPass;
c: var LiftingPass): PNode = c: var LiftingPass): PNode =
var access = setupEnvVar(owner, d, c, n.info) 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] access = c.unownedEnvVars[owner.id]
let obj = access.typ.skipTypes({tyOwned, tyRef}) let obj = access.typ.skipTypes({tyOwned, tyRef})
let field = getFieldFromObj(obj, n.sym) 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) n[0].sons[1] = liftCapturedVars(n[0].sons[1], owner, d, c)
else: else:
n.sons[0] = liftCapturedVars(n[0], owner, d, c) n.sons[0] = liftCapturedVars(n[0], owner, d, c)
of nkTypeOfExpr:
result = n
else: else:
if owner.isIterator: if owner.isIterator:
if nfLL in n.flags: if nfLL in n.flags:
@ -921,7 +923,7 @@ proc liftForLoop*(g: ModuleGraph; body: PNode; owner: PSym): PNode =
result.add(v) result.add(v)
# add 'new' statement: # add 'new' statement:
result.add(newCall(getSysSym(g, env.info, "internalNew"), env.newSymNode)) 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) createTypeBoundOps(g, nil, env.typ, body.info)
elif op.kind == nkStmtListExpr: elif op.kind == nkStmtListExpr:

View file

@ -160,13 +160,11 @@ proc guidingInd(em: Emitter; pos: int): int =
inc i inc i
result = -1 result = -1
proc closeEmitter*(em: var Emitter) = proc renderTokens*(em: var Emitter): string =
## Render Emitter tokens to a string of code
template defaultCase() = template defaultCase() =
content.add em.tokens[i] content.add em.tokens[i]
inc lineLen, em.tokens[i].len inc lineLen, em.tokens[i].len
let outFile = em.config.absOutFile
var content = newStringOfCap(16_000) var content = newStringOfCap(16_000)
var maxLhs = 0 var maxLhs = 0
var lineLen = 0 var lineLen = 0
@ -243,6 +241,11 @@ proc closeEmitter*(em: var Emitter) =
defaultCase() defaultCase()
inc i 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: if fileExists(outFile) and readFile(outFile.string) == content:
discard "do nothing, see #9499" discard "do nothing, see #9499"
return return
@ -253,6 +256,11 @@ proc closeEmitter*(em: var Emitter) =
f.llStreamWrite content f.llStreamWrite content
llStreamClose(f) 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) = proc wr(em: var Emitter; x: string; lt: LayoutToken) =
em.tokens.add x em.tokens.add x
em.kinds.add lt em.kinds.add lt

View file

@ -129,7 +129,7 @@ proc newDeepCopyCall(op: PSym; x, y: PNode): PNode =
result = newAsgnStmt(x, newOpCall(op, y)) result = newAsgnStmt(x, newOpCall(op, y))
proc useNoGc(c: TLiftCtx; t: PType): bool {.inline.} = 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) ({tfHasGCedMem, tfHasOwned} * t.flags != {} or t.isGCedMem)
proc instantiateGeneric(c: var TLiftCtx; op: PSym; t, typeInst: PType): PSym = 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; proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode;
field: var PSym): bool = field: var PSym): bool =
if c.g.config.selectedGC == gcDestructors: if optSeqDestructors in c.g.config.globalOptions:
let op = field let op = field
if field != nil and sfOverriden in field.flags: if field != nil and sfOverriden in field.flags:
if sfError in op.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[0] = newSymNode(createMagic(c.g, "deepCopy", mDeepCopy))
call.sons[1] = y call.sons[1] = y
body.add newAsgnStmt(x, call) 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: optRefCheck in c.g.config.options:
let xx = genBuiltin(c.g, mAccessEnv, "accessEnv", x) let xx = genBuiltin(c.g, mAccessEnv, "accessEnv", x)
xx.typ = getSysType(c.g, c.info, tyPointer) 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: tyPtr, tyOpt, tyUncheckedArray:
defaultOp(c, t, body, x, y) defaultOp(c, t, body, x, y)
of tyRef: of tyRef:
if optNimV2 in c.g.config.globalOptions and if optOwnedRefs in c.g.config.globalOptions and
optRefCheck in c.g.config.options: optRefCheck in c.g.config.options:
weakrefOp(c, t, body, x, y) weakrefOp(c, t, body, x, y)
else: else:
@ -469,7 +469,7 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
defaultOp(c, t, body, x, y) defaultOp(c, t, body, x, y)
of tyOwned: of tyOwned:
let base = t.skipTypes(abstractInstOwned) let base = t.skipTypes(abstractInstOwned)
if optNimV2 in c.g.config.globalOptions: if optOwnedRefs in c.g.config.globalOptions:
case base.kind case base.kind
of tyRef: of tyRef:
ownedRefOp(c, base, body, x, y) ownedRefOp(c, base, body, x, y)
@ -488,7 +488,7 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
of tySequence: of tySequence:
if useNoGc(c, t): if useNoGc(c, t):
useSeqOrStrOp(c, t, body, x, y) 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 # note that tfHasAsgn is propagated so we need the check on
# 'selectedGC' here to determine if we have the new runtime. # 'selectedGC' here to determine if we have the new runtime.
discard considerUserDefinedOp(c, t, body, x, y) discard considerUserDefinedOp(c, t, body, x, y)

View file

@ -81,7 +81,11 @@ type # please make sure we have under 32 options
optNoNimblePath optNoNimblePath
optHotCodeReloading optHotCodeReloading
optDynlibOverrideAll 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 optMultiMethods
optNimV019 optNimV019
optBenchmarkVM # Enables cpuTime() in the VM optBenchmarkVM # Enables cpuTime() in the VM
@ -118,7 +122,7 @@ type
IdeCmd* = enum IdeCmd* = enum
ideNone, ideSug, ideCon, ideDef, ideUse, ideDus, ideChk, ideMod, 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! Feature* = enum ## experimental features; DO NOT RENAME THESE!
implicitDeref, implicitDeref,
@ -662,21 +666,40 @@ proc findModule*(conf: ConfigRef; modulename, currentModule: string): AbsoluteFi
proc findProjectNimFile*(conf: ConfigRef; pkg: string): string = proc findProjectNimFile*(conf: ConfigRef; pkg: string): string =
const extensions = [".nims", ".cfg", ".nimcfg", ".nimble"] const extensions = [".nims", ".cfg", ".nimcfg", ".nimble"]
var candidates: seq[string] = @[] var
var dir = pkg candidates: seq[string] = @[]
dir = pkg
prev = dir
nimblepkg = ""
let pkgname = pkg.lastPathPart()
while true: 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": if k == pcFile and f != "config.nims":
let (_, name, ext) = splitFile(f) let (_, name, ext) = splitFile(f)
if ext in extensions: if ext in extensions:
let x = changeFileExt(dir / name, ".nim") let x = changeFileExt(dir / name, ".nim")
if fileExists(x): if fileExists(x):
candidates.add 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: for c in candidates:
# nim-foo foo or foo nfoo if pkgname in c.extractFilename(): return c
if (pkg in c) or (c in pkg): return c if candidates.len > 0:
if candidates.len >= 1:
return candidates[0] return candidates[0]
prev = dir
dir = parentDir(dir) dir = parentDir(dir)
if dir == "": break if dir == "": break
return "" return ""
@ -709,6 +732,7 @@ proc parseIdeCmd*(s: string): IdeCmd =
of "outline": ideOutline of "outline": ideOutline
of "known": ideKnown of "known": ideKnown
of "msg": ideMsg of "msg": ideMsg
of "project": ideProject
else: ideNone else: ideNone
proc `$`*(c: IdeCmd): string = proc `$`*(c: IdeCmd): string =
@ -725,6 +749,7 @@ proc `$`*(c: IdeCmd): string =
of ideOutline: "outline" of ideOutline: "outline"
of ideKnown: "known" of ideKnown: "known"
of ideMsg: "msg" of ideMsg: "msg"
of ideProject: "project"
proc floatInt64Align*(conf: ConfigRef): int16 = proc floatInt64Align*(conf: ConfigRef): int16 =
## Returns either 4 or 8 depending on reasons. ## Returns either 4 or 8 depending on reasons.

View file

@ -47,12 +47,9 @@ proc demanglePackageName*(path: string): string =
proc withPackageName*(conf: ConfigRef; path: AbsoluteFile): AbsoluteFile = proc withPackageName*(conf: ConfigRef; path: AbsoluteFile): AbsoluteFile =
let x = getPackageName(conf, path.string) let x = getPackageName(conf, path.string)
if x.len == 0: let (p, file, ext) = path.splitFile
result = path if x == "stdlib":
# Hot code reloading now relies on 'stdlib_system' names etc.
result = p / RelativeFile((x & '_' & file) & ext)
else: else:
let (p, file, ext) = path.splitFile result = p / RelativeFile(fakePackageName(conf, path))
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))

View file

@ -720,7 +720,7 @@ proc pragmaGuard(c: PContext; it: PNode; kind: TSymKind): PSym =
proc semCustomPragma(c: PContext, n: PNode): PNode = proc semCustomPragma(c: PContext, n: PNode): PNode =
var callNode: PNode var callNode: PNode
if n.kind == nkIdent: if n.kind in {nkIdent, nkSym}:
# pragma -> pragma() # pragma -> pragma()
callNode = newTree(nkCall, n) callNode = newTree(nkCall, n)
elif n.kind == nkExprColonExpr: elif n.kind == nkExprColonExpr:

View file

@ -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, while result != nil and result.kind in {tyGenericInst, tyRange, tyVar, tyLent, tyDistinct,
tyOrdinal, tyAlias, tySink}: tyOrdinal, tyAlias, tySink}:
result = lastSon(result) result = lastSon(result)
let typ = n.typ.skip let typ = n.typ.skip
if typ != nil and typ.kind in {tyBool, tyEnum}: if typ != nil and typ.kind in {tyBool, tyEnum}:
if sfPure in typ.sym.flags: 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: if e.sym.position == x:
result &= e.sym.name.s result &= e.sym.name.s
return return
let y = x and ((1 shl (size*8)) - 1) if nfBase2 in n.flags: result = "0b" & toBin(x, size * 8)
if nfBase2 in n.flags: result = "0b" & toBin(y, size * 8) elif nfBase8 in n.flags:
elif nfBase8 in n.flags: result = "0o" & toOct(y, size * 3) var y = if size < sizeof(BiggestInt): x and ((1 shl (size*8)) - 1)
elif nfBase16 in n.flags: result = "0x" & toHex(y, size * 2) else: x
result = "0o" & toOct(y, size * 3)
elif nfBase16 in n.flags: result = "0x" & toHex(x, size * 2)
else: result = $x else: result = $x
proc ulitAux(g: TSrcGen; n: PNode, x: BiggestInt, size: int): string = proc ulitAux(g: TSrcGen; n: PNode, x: BiggestInt, size: int): string =
@ -474,7 +477,7 @@ proc lsub(g: TSrcGen; n: PNode): int =
len("if_:_") len("if_:_")
of nkElifExpr: result = lsons(g, n) + len("_elif_:_") of nkElifExpr: result = lsons(g, n) + len("_elif_:_")
of nkElseExpr: result = lsub(g, n.sons[0]) + len("_else:_") # type descriptions 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 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 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") 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, ":") putWithSpace(g, tkColon, ":")
gsub(g, n, 0) gsub(g, n, 0)
of nkTypeOfExpr: of nkTypeOfExpr:
put(g, tkType, "type") put(g, tkType, "typeof")
put(g, tkParLe, "(") put(g, tkParLe, "(")
if n.len > 0: gsub(g, n.sons[0]) if n.len > 0: gsub(g, n.sons[0])
put(g, tkParRi, ")") put(g, tkParRi, ")")

View file

@ -217,7 +217,7 @@ proc runNimScript*(cache: IdentCache; scriptName: AbsoluteFile;
let oldGlobalOptions = conf.globalOptions let oldGlobalOptions = conf.globalOptions
let oldSelectedGC = conf.selectedGC let oldSelectedGC = conf.selectedGC
undefSymbol(conf.symbols, "nimv2") undefSymbol(conf.symbols, "nimv2")
conf.globalOptions.excl optNimV2 conf.globalOptions.excl {optTinyRtti, optOwnedRefs, optSeqDestructors}
conf.selectedGC = gcUnselected conf.selectedGC = gcUnselected
var m = graph.makeModule(scriptName) 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 # watch out, "newruntime" can be set within NimScript itself and then we need
# to remember this: # to remember this:
if optNimV2 in oldGlobalOptions: if optOwnedRefs in oldGlobalOptions:
conf.globalOptions.incl optNimV2 conf.globalOptions.incl {optTinyRtti, optOwnedRefs, optSeqDestructors}
defineSymbol(conf.symbols, "nimv2") defineSymbol(conf.symbols, "nimv2")
if conf.selectedGC == gcUnselected: if conf.selectedGC == gcUnselected:
conf.selectedGC = oldSelectedGC conf.selectedGC = oldSelectedGC

View file

@ -1724,7 +1724,7 @@ proc semReturn(c: PContext, n: PNode): PNode =
result = n result = n
checkSonsLen(n, 1, c.config) checkSonsLen(n, 1, c.config)
if c.p.owner.kind in {skConverter, skMethod, skProc, skFunc, skMacro} or 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: if n.sons[0].kind != nkEmpty:
# transform ``return expr`` to ``result = expr; return`` # transform ``return expr`` to ``result = expr; return``
if c.p.resultSym != nil: if c.p.resultSym != nil:
@ -1771,7 +1771,7 @@ proc semProcBody(c: PContext, n: PNode): PNode =
else: else:
localError(c.config, c.p.resultSym.info, errCannotInferReturnType % localError(c.config, c.p.resultSym.info, errCannotInferReturnType %
c.p.owner.name.s) 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: c.p.owner.typ.sons[0].kind == tyUntyped:
localError(c.config, c.p.owner.info, errCannotInferReturnType % localError(c.config, c.p.owner.info, errCannotInferReturnType %
c.p.owner.name.s) c.p.owner.name.s)
@ -2563,7 +2563,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
# if isGenericRoutine(result.sym): # if isGenericRoutine(result.sym):
# localError(c.config, n.info, errInstantiateXExplicitly, s.name.s) # localError(c.config, n.info, errInstantiateXExplicitly, s.name.s)
# "procs literals" are 'owned' # "procs literals" are 'owned'
if optNimV2 in c.config.globalOptions: if optOwnedRefs in c.config.globalOptions:
result.typ = makeVarType(c, result.typ, tyOwned) result.typ = makeVarType(c, result.typ, tyOwned)
else: else:
result = semSym(c, n, s, flags) result = semSym(c, n, s, flags)

View file

@ -380,7 +380,7 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
t = skipTypes(t, {tyGenericInst, tyAlias, tySink, tyOwned}) t = skipTypes(t, {tyGenericInst, tyAlias, tySink, tyOwned})
if t.kind == tyRef: if t.kind == tyRef:
t = skipTypes(t.sons[0], {tyGenericInst, tyAlias, tySink, tyOwned}) 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) result.typ = makeVarType(c, result.typ, tyOwned)
# we have to watch out, there are also 'owned proc' types that can be used # 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. # multiple times as long as they don't have closures.

View file

@ -38,7 +38,6 @@ const
errPragmaOnlyInHeaderOfProcX = "pragmas are only allowed in the header of a proc; redefinition of $1" 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 '()'?" errCannotAssignMacroSymbol = "cannot assign macro symbol to $1 here. Forgot to invoke the macro with '()'?"
errInvalidTypeDescAssign = "'typedesc' metatype is not valid here; typed '=' instead of ':'?" 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 = proc semDiscard(c: PContext, n: PNode): PNode =
result = n result = n
@ -456,9 +455,6 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
if def.sym.kind == skMacro: if def.sym.kind == skMacro:
localError(c.config, def.info, errCannotAssignMacroSymbol % "variable") localError(c.config, def.info, errCannotAssignMacroSymbol % "variable")
def.typ = errorType(c) 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: elif def.typ.kind == tyTypeDesc and c.p.owner.kind != skMacro:
# prevent the all too common 'var x = int' bug: # prevent the all too common 'var x = int' bug:
localError(c.config, def.info, errInvalidTypeDescAssign) localError(c.config, def.info, errInvalidTypeDescAssign)
@ -601,9 +597,6 @@ proc semConst(c: PContext, n: PNode): PNode =
if def.sym.kind == skMacro: if def.sym.kind == skMacro:
localError(c.config, def.info, errCannotAssignMacroSymbol % "constant") localError(c.config, def.info, errCannotAssignMacroSymbol % "constant")
def.typ = errorType(c) 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: elif def.typ.kind == tyTypeDesc and c.p.owner.kind != skMacro:
# prevent the all too common 'const x = int' bug: # prevent the all too common 'const x = int' bug:
localError(c.config, def.info, errInvalidTypeDescAssign) 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 closeScope(c) # close scope for parameters
popOwner(c) popOwner(c)
result.typ = s.typ result.typ = s.typ
if optNimV2 in c.config.globalOptions: if optOwnedRefs in c.config.globalOptions:
result.typ = makeVarType(c, result.typ, tyOwned) result.typ = makeVarType(c, result.typ, tyOwned)
proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode = proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode =
@ -1563,7 +1556,7 @@ proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode =
popProcCon(c) popProcCon(c)
popOwner(c) popOwner(c)
closeScope(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) result.typ = makeVarType(c, result.typ, tyOwned)
# alternative variant (not quite working): # alternative variant (not quite working):
# var prc = arg[0].sym # 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") let resultType = sysTypeFromName(c.graph, n.info, "NimNode")
addResult(c, resultType, n.info, s.kind) addResult(c, resultType, n.info, s.kind)
addResultNode(c, n) 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) addResult(c, s.typ.sons[0], n.info, s.kind)
addResultNode(c, n) addResultNode(c, n)
@ -1955,7 +1948,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
if isAnon: if isAnon:
n.kind = nkLambda n.kind = nkLambda
result.typ = s.typ result.typ = s.typ
if optNimV2 in c.config.globalOptions: if optOwnedRefs in c.config.globalOptions:
result.typ = makeVarType(c, result.typ, tyOwned) result.typ = makeVarType(c, result.typ, tyOwned)
if isTopLevel(c) and s.kind != skIterator and if isTopLevel(c) and s.kind != skIterator and
s.typ.callConv == ccClosure: s.typ.callConv == ccClosure:
@ -1969,7 +1962,6 @@ proc determineType(c: PContext, s: PSym) =
proc semIterator(c: PContext, n: PNode): PNode = proc semIterator(c: PContext, n: PNode): PNode =
# gensym'ed iterator? # gensym'ed iterator?
let isAnon = n[namePos].kind == nkEmpty
if n[namePos].kind == nkSym: if n[namePos].kind == nkSym:
# gensym'ed iterators might need to become closure iterators: # gensym'ed iterators might need to become closure iterators:
n[namePos].sym.owner = getCurrOwner(c) n[namePos].sym.owner = getCurrOwner(c)
@ -1983,8 +1975,6 @@ proc semIterator(c: PContext, n: PNode): PNode =
var t = s.typ var t = s.typ
if t.sons[0] == nil and s.typ.callConv != ccClosure: if t.sons[0] == nil and s.typ.callConv != ccClosure:
localError(c.config, n.info, "iterator needs a return type") 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, # iterators are either 'inline' or 'closure'; for backwards compatibility,
# we require first class iterators to be marked with 'closure' explicitly # we require first class iterators to be marked with 'closure' explicitly
# -- at least for 0.9.2. # -- at least for 0.9.2.

View file

@ -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) errorUndeclaredIdentifier(c, n.sons[0].info, typ.sym.name.s)
elif not isOrdinalType(typ): elif not isOrdinalType(typ):
localError(c.config, n.sons[0].info, "selector must be of an ordinal type, float or string") 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): for i in 1 ..< len(n):
var b = copyTree(n.sons[i]) var b = copyTree(n.sons[i])
addSon(a, b) addSon(a, b)
@ -871,7 +877,7 @@ proc semAnyRef(c: PContext; n: PNode; kind: TTypeKind; prev: PType): PType =
if tfPartial in result.flags: if tfPartial in result.flags:
if result.lastSon.kind == tyObject: incl(result.lastSon.flags, tfPartial) if result.lastSon.kind == tyObject: incl(result.lastSon.flags, tfPartial)
#if not isNilable: result.flags.incl tfNotNil #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) let t = newTypeS(tyOwned, c)
t.flags.incl tfHasOwned t.flags.incl tfHasOwned
t.rawAddSonNoPropagationOfTypeFlags result t.rawAddSonNoPropagationOfTypeFlags result
@ -1638,7 +1644,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
result = semTypeof(c, n[1], prev) result = semTypeof(c, n[1], prev)
elif op.s == "typeof" and n[0].kind == nkSym and n[0].sym.magic == mTypeOf: elif op.s == "typeof" and n[0].kind == nkSym and n[0].sym.magic == mTypeOf:
result = semTypeof2(c, n, prev) 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) result = semTypeExpr(c, n[1], prev)
else: else:
if c.inGenericContext > 0 and n.kind == nkCall: 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 mSet: result = semSet(c, n, prev)
of mOrdinal: result = semOrdinal(c, n, prev) of mOrdinal: result = semOrdinal(c, n, prev)
of mSeq: of mSeq:
if false: # c.config.selectedGC == gcDestructors and optNimV2 notin c.config.globalOptions: result = semContainer(c, n, tySequence, "seq", prev)
let s = c.graph.sysTypes[tySequence] if optSeqDestructors in c.config.globalOptions:
assert s != nil incl result.flags, tfHasAsgn
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
of mOpt: result = semContainer(c, n, tyOpt, "opt", prev) of mOpt: result = semContainer(c, n, tyOpt, "opt", prev)
of mVarargs: result = semVarargs(c, n, prev) of mVarargs: result = semVarargs(c, n, prev)
of mTypeDesc, mType, mTypeOf: of mTypeDesc, mType, mTypeOf:
@ -1856,7 +1845,7 @@ proc processMagicType(c: PContext, m: PSym) =
of mString: of mString:
setMagicType(c.config, m, tyString, szUncomputedSize) setMagicType(c.config, m, tyString, szUncomputedSize)
rawAddSon(m.typ, getSysType(c.graph, m.info, tyChar)) 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 incl m.typ.flags, tfHasAsgn
of mCstring: of mCstring:
setMagicIntegral(c.config, m, tyCString, c.config.target.ptrSize) 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) setMagicIntegral(c.config, m, tyUncheckedArray, szUncomputedSize)
of mSeq: of mSeq:
setMagicType(c.config, m, tySequence, szUncomputedSize) setMagicType(c.config, m, tySequence, szUncomputedSize)
if c.config.selectedGC == gcDestructors: if optSeqDestructors in c.config.globalOptions:
incl m.typ.flags, tfHasAsgn incl m.typ.flags, tfHasAsgn
assert c.graph.sysTypes[tySequence] == nil assert c.graph.sysTypes[tySequence] == nil
c.graph.sysTypes[tySequence] = m.typ c.graph.sysTypes[tySequence] = m.typ

View file

@ -2590,11 +2590,6 @@ proc instTypeBoundOp*(c: PContext; dc: PSym; t: PType; info: TLineInfo;
if f.kind in {tyRef, tyPtr}: f = f.lastSon if f.kind in {tyRef, tyPtr}: f = f.lastSon
else: else:
if f.kind == tyVar: f = f.lastSon 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: if typeRel(m, f, t) == isNone:
localError(c.config, info, "cannot instantiate: '" & dc.name.s & "'") localError(c.config, info, "cannot instantiate: '" & dc.name.s & "'")
else: else:

View file

@ -231,7 +231,7 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) =
typ.size = conf.target.ptrSize typ.size = conf.target.ptrSize
typ.align = int16(conf.target.ptrSize) typ.align = int16(conf.target.ptrSize)
of tyString: of tyString:
if conf.selectedGC == gcDestructors: if optSeqDestructors in conf.globalOptions:
typ.size = conf.target.ptrSize * 2 typ.size = conf.target.ptrSize * 2
else: else:
typ.size = conf.target.ptrSize typ.size = conf.target.ptrSize
@ -245,7 +245,7 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) =
typ.paddingAtEnd = szIllegalRecursion typ.paddingAtEnd = szIllegalRecursion
return return
typ.align = int16(conf.target.ptrSize) 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 typ.size = conf.target.ptrSize * 2
else: else:
typ.size = conf.target.ptrSize typ.size = conf.target.ptrSize

View file

@ -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] vpart.sons[2] = if varInit.isNil: v else: vpart[1]
varSection.add vpart varSection.add vpart
if varInit != nil: 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) varInit.add newFastAsgnStmt(newSymNode(result), v)
else: else:
let deepCopyCall = newNodeI(nkCall, varInit.info, 3) let deepCopyCall = newNodeI(nkCall, varInit.info, 3)

View file

@ -914,7 +914,7 @@ proc transform(c: PTransf, n: PNode): PTransNode =
oldDeferAnchor = c.deferAnchor oldDeferAnchor = c.deferAnchor
c.deferAnchor = n c.deferAnchor = n
if (n.typ != nil and tfHasAsgn in n.typ.flags) or 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 c.needsDestroyPass = true
case n.kind case n.kind
of nkSym: of nkSym:

View file

@ -1121,9 +1121,16 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
result = sameChildrenAux(a, b, c) and sameFlags(a, b) result = sameChildrenAux(a, b, c) and sameFlags(a, b)
if result and {ExactGenericParams, ExactTypeDescValues} * c.flags != {}: if result and {ExactGenericParams, ExactTypeDescValues} * c.flags != {}:
result = a.sym.position == b.sym.position result = a.sym.position == b.sym.position
of tyGenericInvocation, tyGenericBody, tySequence, of tyBuiltInTypeClass:
tyOpenArray, tySet, tyRef, tyPtr, tyVar, tyLent, tySink, tyUncheckedArray, assert a.len == 1
tyArray, tyProc, tyVarargs, tyOrdinal, tyTypeClasses, tyOpt, tyOwned: 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() cycleCheck()
if a.kind == tyUserTypeClass and a.n != nil: return a.n == b.n if a.kind == tyUserTypeClass and a.n != nil: return a.n == b.n
result = sameChildrenAux(a, b, c) 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 if kind notin {skParam, skResult}: result = t
else: result = typeAllowedAux(marker, t2, kind, flags) else: result = typeAllowedAux(marker, t2, kind, flags)
of tyProc: 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 let f = if kind in {skProc, skFunc}: flags+{taNoUntyped} else: flags
for i in 1 ..< len(t): for i in 1 ..< len(t):
result = typeAllowedAux(marker, t.sons[i], skParam, f-{taIsOpenArray})
if result != nil: break if result != nil: break
result = typeAllowedAux(marker, t.sons[i], skParam, f-{taIsOpenArray})
if result.isNil and t.sons[0] != nil: if result.isNil and t.sons[0] != nil:
result = typeAllowedAux(marker, t.sons[0], skResult, flags) result = typeAllowedAux(marker, t.sons[0], skResult, flags)
of tyTypeDesc: of tyTypeDesc:

View file

@ -710,7 +710,6 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
of rkNode: of rkNode:
if regs[ra].node.kind == nkNilLit: if regs[ra].node.kind == nkNilLit:
stackTrace(c, tos, pc, errNilAccess) stackTrace(c, tos, pc, errNilAccess)
assert nfIsRef in regs[ra].node.flags
regs[ra].node[] = regs[rc].regToNode[] regs[ra].node[] = regs[rc].regToNode[]
regs[ra].node.flags.incl nfIsRef regs[ra].node.flags.incl nfIsRef
else: stackTrace(c, tos, pc, errNilAccess) else: stackTrace(c, tos, pc, errNilAccess)

View file

@ -948,9 +948,13 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
c.genAddSubInt(n, dest, opcAddInt) c.genAddSubInt(n, dest, opcAddInt)
of mInc, mDec: of mInc, mDec:
unused(c, n, dest) 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]) 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) c.gABI(n, succ(opc), d, d, n.sons[2].intVal)
else: else:
let tmp = c.genx(n.sons[2]) let tmp = c.genx(n.sons[2])

View file

@ -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" 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 @end
gcc.options.speed = "-O3 -fno-strict-aliasing" gcc.options.speed = "-O3 -fno-strict-aliasing -fno-ident"
gcc.options.size = "-Os" gcc.options.size = "-Os -fno-ident"
@if windows: @if windows:
gcc.options.debug = "-g3 -Og -gdwarf-3" gcc.options.debug = "-g3 -Og -gdwarf-3"
@else: @else:
gcc.options.debug = "-g3 -Og" gcc.options.debug = "-g3 -Og"
@end @end
gcc.cpp.options.speed = "-O3 -fno-strict-aliasing" gcc.cpp.options.speed = "-O3 -fno-strict-aliasing -fno-ident"
gcc.cpp.options.size = "-Os" gcc.cpp.options.size = "-Os -fno-ident"
gcc.cpp.options.debug = "-g3 -Og" gcc.cpp.options.debug = "-g3 -Og"
#passl = "-pg" #passl = "-pg"
@ -257,7 +257,8 @@ vcc.linkerexe = "vccexe.exe"
vcc.cpp.linkerexe = "vccexe.exe" vcc.cpp.linkerexe = "vccexe.exe"
vcc.cpp.options.always = "/EHsc" 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.options.debug = "/Zi /FS /Od"
vcc.cpp.options.debug = "/Zi /FS /Od" vcc.cpp.options.debug = "/Zi /FS /Od"
vcc.options.speed = "/O2" vcc.options.speed = "/O2"

View file

@ -89,6 +89,8 @@ Advanced options:
strings is allowed; only for backwards compatibility strings is allowed; only for backwards compatibility
--nilseqs:on|off allow 'nil' for strings/seqs for --nilseqs:on|off allow 'nil' for strings/seqs for
backwards compatibility backwards compatibility
--seqsv2:on|off use the new string/seq implementation based on
destructors
--oldast:on|off use old AST for backwards compatibility --oldast:on|off use old AST for backwards compatibility
--skipCfg:on|off do not read the nim installation's configuration file --skipCfg:on|off do not read the nim installation's configuration file
--skipUserCfg:on|off do not read the user's configuration file --skipUserCfg:on|off do not read the user's configuration file

View file

@ -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 being freed by the GC. Other useful procs from `system <system.html>`_ you can
use to keep track of memory are: use to keep track of memory are:
* getTotalMem(): returns the amount of total memory managed by the GC. * ``getTotalMem()`` Returns the amount of total memory managed by the GC.
* getOccupiedMem(): bytes reserved by the GC and used by objects. * ``getOccupiedMem()`` Bytes reserved by the GC and used by objects.
* getFreeMem(): bytes reserved by the GC and not in use. * ``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 In addition to ``GC_ref`` and ``GC_unref`` you can avoid the GC by manually
allocating memory with procs like ``alloc``, ``allocShared``, or 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 all running threads, not only to the current thread. (The current thread
would be the thread that calls ``dumpNumberOfInstances``.) This might would be the thread that calls ``dumpNumberOfInstances``.) This might
change in later versions. 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.

View file

@ -3511,8 +3511,8 @@ for details on how to change this behavior.
Anonymous Procs Anonymous Procs
--------------- ---------------
Procs can also be treated as expressions, in which case it's allowed to omit Unnamed procedures can be used as lambda expressions to pass into other
the proc's name. procedures:
.. code-block:: nim .. code-block:: nim
var cities = @["Frankfurt", "Tokyo", "New York", "Kyiv"] 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 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 Func
@ -3694,7 +3696,7 @@ type.
method eval(e: Expression): int {.base.} = method eval(e: Expression): int {.base.} =
# override this base method # override this base method
quit "to override!" raise newException(CatchableError, "Method without implementation override")
method eval(e: Literal): int = return e.x method eval(e: Literal): int = return e.x
@ -6222,6 +6224,25 @@ but are used to override the settings temporarily. Example:
# ... some code ... # ... some code ...
{.pop.} # restore old settings {.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 register pragma
--------------- ---------------

View file

@ -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 Numbers
------- -------

View file

@ -320,8 +320,8 @@ proc boot(args: string) =
# jsonbuild then uses the $project.json file to build the Nim binary. # jsonbuild then uses the $project.json file to build the Nim binary.
exec "$# $# $# $# --nimcache:$# --compileOnly compiler" / "nim.nim" % exec "$# $# $# $# --nimcache:$# --compileOnly compiler" / "nim.nim" %
[nimi, bootOptions, extraOption, args, smartNimcache] [nimi, bootOptions, extraOption, args, smartNimcache]
exec "$# jsonscript --nimcache:$# compiler" / "nim.nim" % exec "$# jsonscript $# --nimcache:$# compiler" / "nim.nim" %
[nimi, smartNimcache] [nimi, args, smartNimcache]
if sameFileContent(output, i.thVersion): if sameFileContent(output, i.thVersion):
copyExe(output, finalDest) copyExe(output, finalDest)

View file

@ -94,6 +94,18 @@
## ##
## db.close() ## 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 ## See also
## ======== ## ========
## ##

View file

@ -24,6 +24,7 @@ proc logImpl(console: Console) {.importcpp: "log", varargs.}
proc debugImpl(console: Console) {.importcpp: "debug", varargs.} proc debugImpl(console: Console) {.importcpp: "debug", varargs.}
proc infoImpl(console: Console) {.importcpp: "info", varargs.} proc infoImpl(console: Console) {.importcpp: "info", varargs.}
proc errorImpl(console: Console) {.importcpp: "error", varargs.} proc errorImpl(console: Console) {.importcpp: "error", varargs.}
proc warnImpl(console: Console) {.importcpp: "warn", varargs.}
proc makeConsoleCall(console: NimNode, procName: NimNode, args: NimNode): NimNode = proc makeConsoleCall(console: NimNode, procName: NimNode, args: NimNode): NimNode =
result = newCall(procName, console) 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 = macro error*(console: Console, args: varargs[RootRef, convertToConsoleLoggable]): untyped =
makeConsoleCall(console, bindSym "errorImpl", args) 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

View file

@ -56,100 +56,119 @@
const const
cb64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" cb64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
invalidChar = 255
template encodeInternal(s: typed, lineLen: int, newLine: string): untyped = template encodeInternal(s: typed): untyped =
## encodes `s` into base64 representation. After `lineLen` characters, a ## encodes `s` into base64 representation.
## `newline` is added. proc encodeSize(size: int): int =
var total = ((len(s) + 2) div 3) * 4 return (size * 4 div 3) + 6
let numLines = (total + lineLen - 1) div lineLen
if numLines > 0: inc(total, (numLines - 1) * newLine.len) result.setLen(encodeSize(s.len))
result = newString(total)
var var
i = 0 inputIndex = 0
r = 0 outputIndex = 0
currLine = 0 inputEnds = s.len - s.len mod 3
while i < s.len - 2: n: uint32
let b: uint32
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
if i < s.len-1: template inputByte(exp: untyped) =
let b = uint32(s[inputIndex])
a = ord(s[i]) n = exp
b = ord(s[i+1]) inc inputIndex
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
proc encode*[T: SomeInteger|char](s: openArray[T], lineLen = 75, template outputChar(x: untyped) =
newLine = ""): string = result[outputIndex] = cb64[x and 63]
## Encodes ``s`` into base64 representation. After ``lineLen`` characters, a inc outputIndex
## ``newline`` is added.
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 ## This procedure encodes an openarray (array or sequence) of either integers
## or characters. ## or characters.
## ##
## **See also:** ## **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 ## * `decode proc<#decode,string>`_ for decoding a string
runnableExamples: runnableExamples:
assert encode(['n', 'i', 'm']) == "bmlt" assert encode(['n', 'i', 'm']) == "bmlt"
assert encode(@['n', 'i', 'm']) == "bmlt" assert encode(@['n', 'i', 'm']) == "bmlt"
assert encode([1, 2, 3, 4, 5]) == "AQIDBAU=" assert encode([1, 2, 3, 4, 5]) == "AQIDBAU="
encodeInternal(s, lineLen, newLine) encodeInternal(s)
proc encode*(s: string, lineLen = 75, newLine = ""): string = proc encode*(s: string): string =
## Encodes ``s`` into base64 representation. After ``lineLen`` characters, a ## Encodes ``s`` into base64 representation.
## ``newline`` is added.
## ##
## This procedure encodes a string. ## This procedure encodes a string.
## ##
## **See also:** ## **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 ## * `decode proc<#decode,string>`_ for decoding a string
runnableExamples: runnableExamples:
assert encode("Hello World") == "SGVsbG8gV29ybGQ=" assert encode("Hello World") == "SGVsbG8gV29ybGQ="
assert encode("Hello World", 3, "\n") == "SGVs\nbG8g\nV29ybGQ=" encodeInternal(s)
encodeInternal(s, lineLen, newLine)
proc decodeByte(b: char): int {.inline.} = proc encodeMIME*(s: string, lineLen = 75, newLine = "\r\n"): string =
case b ## Encodes ``s`` into base64 representation as lines.
of '+': result = ord('>') ## Used in email MIME forma, use ``lineLen`` and ``newline``.
of '0'..'9': result = ord(b) + 4 ##
of 'A'..'Z': result = ord(b) - ord('A') ## This procedure encodes a string according to MIME spec.
of 'a'..'z': result = ord(b) - 71 ##
else: result = 63 ## **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 = proc decode*(s: string): string =
## Decodes string ``s`` in base64 representation back into its original form. ## Decodes string ``s`` in base64 representation back into its original form.
@ -161,53 +180,58 @@ proc decode*(s: string): string =
runnableExamples: runnableExamples:
assert decode("SGVsbG8gV29ybGQ=") == "Hello World" assert decode("SGVsbG8gV29ybGQ=") == "Hello World"
assert decode(" SGVsbG8gV29ybGQ=") == "Hello World" assert decode(" SGVsbG8gV29ybGQ=") == "Hello World"
const Whitespace = {' ', '\t', '\v', '\r', '\l', '\f'} if s.len == 0: return
var total = ((len(s) + 3) div 4) * 3
# total is an upper bound, as we will skip arbitrary whitespace:
result = newString(total)
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 var
i = 0 inputIndex = 0
r = 0 outputIndex = 0
while true: inputLen = s.len
while i < s.len and s[i] in Whitespace: inc(i) inputEnds = 0
if i < s.len-3: # strip trailing characters
let while s[inputLen - 1] in {'\n', '\r', ' ', '='}:
a = s[i].decodeByte dec inputLen
b = s[i+1].decodeByte # hot loop: read 4 characters at at time
c = s[i+2].decodeByte inputEnds = inputLen - 4
d = s[i+3].decodeByte 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

View file

@ -387,6 +387,7 @@ proc assign*(dest: var IntSet, src: IntSet) =
else: else:
dest.counter = src.counter dest.counter = src.counter
dest.max = src.max dest.max = src.max
dest.elems = src.elems
newSeq(dest.data, src.data.len) newSeq(dest.data, src.data.len)
var it = src.head var it = src.head
@ -653,3 +654,19 @@ when isMainModule:
xs = toSeq(items(x)) xs = toSeq(items(x))
xs.sort(cmp[int]) xs.sort(cmp[int])
assert xs == @[1, 4, 7, 1001, 1056] 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()

View file

@ -1019,9 +1019,9 @@ when isMainModule and not defined(release):
# --> {1, 3, 5} # --> {1, 3, 5}
block toSeqAndString: block toSeqAndString:
var a = toHashSet([2, 4, 5]) var a = toHashSet([2, 7, 5])
var b = initHashSet[int]() 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) assert($a == $b)
#echo a #echo a
#echo toHashSet(["no", "esc'aping", "is \" provided"]) #echo toHashSet(["no", "esc'aping", "is \" provided"])

View file

@ -112,29 +112,32 @@ proc hash*[T: proc](x: T): Hash {.inline.} =
else: else:
result = hash(pointer(x)) result = hash(pointer(x))
const
prime = uint(11)
proc hash*(x: int): Hash {.inline.} = proc hash*(x: int): Hash {.inline.} =
## Efficient hashing of integers. ## Efficient hashing of integers.
result = x result = cast[Hash](cast[uint](x) * prime)
proc hash*(x: int64): Hash {.inline.} = proc hash*(x: int64): Hash {.inline.} =
## Efficient hashing of `int64` integers. ## Efficient hashing of `int64` integers.
result = cast[int](x) result = cast[Hash](cast[uint](x) * prime)
proc hash*(x: uint): Hash {.inline.} = proc hash*(x: uint): Hash {.inline.} =
## Efficient hashing of unsigned integers. ## Efficient hashing of unsigned integers.
result = cast[int](x) result = cast[Hash](x * prime)
proc hash*(x: uint64): Hash {.inline.} = proc hash*(x: uint64): Hash {.inline.} =
## Efficient hashing of `uint64` integers. ## Efficient hashing of `uint64` integers.
result = cast[int](x) result = cast[Hash](cast[uint](x) * prime)
proc hash*(x: char): Hash {.inline.} = proc hash*(x: char): Hash {.inline.} =
## Efficient hashing of characters. ## Efficient hashing of characters.
result = ord(x) result = cast[Hash](cast[uint](ord(x)) * prime)
proc hash*[T: Ordinal](x: T): Hash {.inline.} = proc hash*[T: Ordinal](x: T): Hash {.inline.} =
## Efficient hashing of other ordinal types (e.g. enums). ## 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.} = proc hash*(x: float): Hash {.inline.} =
## Efficient hashing of floats. ## Efficient hashing of floats.

View file

@ -10,21 +10,33 @@
## Do yourself a favor and import the module ## Do yourself a favor and import the module
## as ``from htmlgen import nil`` and then fully qualify the macros. ## 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 ## This module implements a simple `XML`:idx: and `HTML`:idx: code
## generator. Each commonly used HTML tag has a corresponding macro ## generator. Each commonly used HTML tag has a corresponding macro
## that generates a string with its HTML representation. ## 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 ## Examples
## ======== ## ========
## ##
## .. code-block:: Nim ## .. code-block:: Nim
## var nim = "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:: ## Writes the string::
## ##
## <h1><a href="http://nim-lang.org">Nim</a></h1> ## <h1><a href="https://nim-lang.org">Nim</a></h1>
## ##
import import
@ -604,9 +616,195 @@ macro wbr*(e: varargs[untyped]): untyped =
## generates the HTML ``wbr`` element. ## generates the HTML ``wbr`` element.
result = xmlCheckedTag(e, "wbr", commonAttr, "", true) 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: runnableExamples:
let nim = "Nim" let nim = "Nim"
assert h1(a(href = "http://nim-lang.org", nim)) == assert h1(a(href = "https://nim-lang.org", nim)) ==
"""<h1><a href="http://nim-lang.org">Nim</a></h1>""" """<h1><a href="https://nim-lang.org">Nim</a></h1>"""
assert form(action = "test", `accept-charset` = "Content-Type") == assert form(action = "test", `accept-charset` = "Content-Type") ==
"""<form action="test" accept-charset="Content-Type"></form>""" """<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>"""

View file

@ -454,7 +454,7 @@ proc generateHeaders(requestUrl: Uri, httpMethod: string,
# Proxy auth header. # Proxy auth header.
if not proxy.isNil and proxy.auth != "": 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") add(result, "Proxy-Authorization: basic " & auth & "\c\L")
for key, val in headers: for key, val in headers:

View file

@ -833,50 +833,37 @@ proc parseJson(p: var JsonParser): JsonNode =
of tkError, tkCurlyRi, tkBracketRi, tkColon, tkComma, tkEof: of tkError, tkCurlyRi, tkBracketRi, tkColon, tkComma, tkEof:
raiseParseErr(p, "{") raiseParseErr(p, "{")
when not defined(js): iterator parseJsonFragments*(s: Stream, filename: string = ""): JsonNode =
iterator parseJsonFragments*(s: Stream, filename: string = ""): JsonNode = ## Parses from a stream `s` into `JsonNodes`. `filename` is only needed
## Parses from a stream `s` into `JsonNodes`. `filename` is only needed ## for nice error messages.
## for nice error messages. ## The JSON fragments are separated by whitespace. This can be substantially
## The JSON fragments are separated by whitespace. This can be substantially ## faster than the comparable loop
## faster than the comparable loop ## ``for x in splitWhitespace(s): yield parseJson(x)``.
## ``for x in splitWhitespace(s): yield parseJson(x)``. ## This closes the stream `s` after it's done.
## This closes the stream `s` after it's done. var p: JsonParser
var p: JsonParser p.open(s, filename)
p.open(s, filename) try:
try: discard getTok(p) # read first token
discard getTok(p) # read first token while p.tok != tkEof:
while p.tok != tkEof: yield p.parseJson()
yield p.parseJson() finally:
finally: p.close()
p.close()
proc parseJson*(s: Stream, filename: string = ""): JsonNode = proc parseJson*(s: Stream, filename: string = ""): JsonNode =
## Parses from a stream `s` into a `JsonNode`. `filename` is only needed ## Parses from a stream `s` into a `JsonNode`. `filename` is only needed
## for nice error messages. ## for nice error messages.
## If `s` contains extra data, it will raise `JsonParsingError`. ## If `s` contains extra data, it will raise `JsonParsingError`.
## This closes the stream `s` after it's done. ## This closes the stream `s` after it's done.
var p: JsonParser var p: JsonParser
p.open(s, filename) p.open(s, filename)
try: try:
discard getTok(p) # read first token discard getTok(p) # read first token
result = p.parseJson() result = p.parseJson()
eat(p, tkEof) # check if there is no extra data eat(p, tkEof) # check if there is no extra data
finally: finally:
p.close() p.close()
proc parseJson*(buffer: string): JsonNode = when defined(js):
## 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:
from math import `mod` from math import `mod`
type type
JSObject = object JSObject = object
@ -946,38 +933,32 @@ else:
result = newJNull() result = newJNull()
proc parseJson*(buffer: string): JsonNode = 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, proc parseFile*(filename: string): JsonNode =
index: string | int | NimNode): NimNode ## Parses `file` into a `JsonNode`.
{.compileTime.} = ## If `file` contains extra data, it will raise `JsonParsingError`.
when index is string: var stream = newFileStream(filename, fmRead)
let indexNode = newStrLitNode(index) if stream == nil:
elif index is int: raise newException(IOError, "cannot read from file: " & filename)
let indexNode = newIntLitNode(index) result = parseJson(stream, filename)
elif index is NimNode:
let indexNode = index
result = newNimNode(nnkBracketExpr).add( # -- Json deserialiser. --
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))
template verifyJsonKind(node: JsonNode, kinds: set[JsonNodeKind], template verifyJsonKind(node: JsonNode, kinds: set[JsonNodeKind],
ast: string) = 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'." % [ let msg = "Incorrect JSON kind. Wanted '$1' in '$2' but got '$3'." % [
$kinds, $kinds,
ast, ast,
@ -985,588 +966,232 @@ template verifyJsonKind(node: JsonNode, kinds: set[JsonNodeKind],
] ]
raise newException(JsonKindError, msg) 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 = when defined(nimFixedForwardGeneric):
## Converts a Sym type node (returned by getType et al.) into an # The following forward declarations don't work in older versions of Nim
## 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
proc createGetEnumCall(jsonNode, kindType: NimNode): NimNode = # forward declare all initFromJson
# -> getEnum(`jsonNode`, `kindType`)
result = newCall(bindSym("getEnum"), jsonNode, toStrLit(jsonNode), kindType)
proc createOfBranchCond(ofBranch, getEnumCall: NimNode): NimNode = proc initFromJson(dst: var string; jsonNode: JsonNode; jsonPath: string)
## Creates an expression that acts as the condition for an ``of`` branch. proc initFromJson(dst: var bool; jsonNode: JsonNode; jsonPath: string)
var cond = newIdentNode("false") proc initFromJson(dst: var JsonNode; jsonNode: JsonNode; jsonPath: string)
for ofCond in ofBranch: proc initFromJson[T: SomeInteger](dst: var T; jsonNode: JsonNode, jsonPath: string)
if ofCond.kind == nnkRecList: proc initFromJson[T: SomeFloat](dst: var T; jsonNode: JsonNode; jsonPath: string)
break 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) # initFromJson definitions
cond = infix(cond, "or", comparison)
return cond proc initFromJson(dst: var string; jsonNode: JsonNode; jsonPath: string) =
verifyJsonKind(jsonNode, {JString, JNull}, jsonPath)
proc processObjField(field, jsonNode: NimNode): seq[NimNode] {.compileTime.} # since strings don't have a nil state anymore, this mapping of
proc processOfBranch(ofBranch, jsonNode, kindType, # JNull to the default string is questionable. `none(string)` and
kindJsonNode: NimNode): seq[NimNode] {.compileTime.} = # `some("")` have the same potentional json value `JNull`.
## Processes each field inside of an object's ``of`` branch. if jsonNode.kind == JNull:
## For each field a new ExprColonExpr node is created and put in the dst = ""
## 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))
else: 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 discard
of nnkTupleTy: of nnkRecList, nnkTupleTy:
for identDefs in obj: for it in typeNode:
expectKind(identDefs, nnkIdentDefs) foldObjectBody(dst, it, tmpSym, jsonNode, jsonPath, depth + 1)
let nodes = processObjField(identDefs[0], jsonNode)
result.add(nodes)
else:
doAssert false, "Unable to process field type: " & $obj.kind
proc processType(typeName: NimNode, obj: NimNode, of nnkIdentDefs:
jsonNode: NimNode, isRef: bool): NimNode {.compileTime.} = typeNode.expectLen 3
## Process a type such as ``Sym "float"`` or ``ObjectTy ...``. let fieldSym = typeNode[0]
## let fieldNameLit = newLit(fieldSym.strVal)
## Sample ``ObjectTy``: let fieldType = typeNode[1]
##
## .. 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)
if obj.kind == nnkObjectTy: # Detecting incompatiple tuple types in `assignObjectImpl` only
result.add(typeName) # Name of the type to construct. # 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 dst.add quote do:
result.add(processFields(obj, jsonNode)) initFromJson(`tmpSym`.`fieldSym`, getOrDefault(`jsonNode`,`fieldNameLit`), `jsonPath` & "." & `fieldNameLit`)
# Object might be null. So we need to check for that. of nnkRecCase:
if isRef: let kindSym = typeNode[0][0]
result = quote do: let kindNameLit = newLit(kindSym.strVal)
verifyJsonKind(`jsonNode`, {JObject, JNull}, astToStr(`jsonNode`)) let kindType = typeNode[0][1]
if `jsonNode`.kind == JNull: let kindOffsetLit = newLit(uint(getOffset(kindSym)))
nil dst.add quote do:
var kindTmp: `kindType`
initFromJson(kindTmp, `jsonNode`[`kindNameLit`], `jsonPath` & "." & `kindNameLit`)
when defined js:
`tmpSym`.`kindSym` = kindTmp
else: 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: else:
result = quote do: error("unhandled kind: " & $typeNode.kind, typeNode)
verifyJsonKind(`jsonNode`, {JObject}, astToStr(`jsonNode`));
`result`
of nnkEnumTy:
let instType = toIdentNode(getTypeInst(typeName)) macro assignObjectImpl[T](dst: var T; jsonNode: JsonNode; jsonPath: string) =
let getEnumCall = createGetEnumCall(jsonNode, instType) let typeSym = getTypeInst(dst)
result = quote do: result = newStmtList()
( if typeSym.kind in {nnkTupleTy, nnkTupleConstr}:
`getEnumCall` # both, `dst` and `typeSym` don't have good lineinfo. But nothing
) # else is available here.
of nnkSym: detectIncompatibleType(typeSym, dst)
let name = normalize($typeName.getTypeImpl()) foldObjectBody(result, typeSym, dst, jsonNode, jsonPath, 0)
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
)
else: else:
if name.startsWith("int") or name.startsWith("uint"): foldObjectBody(result, typeSym.getTypeImpl, dst, jsonNode, jsonPath, 0)
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
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 to*[T](node: JsonNode, t: typedesc[T]): T =
proc workaroundMacroNone[T](): Option[T] = ## `Unmarshals`:idx: the specified node into the object type specified.
none(T) ##
## 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 = initFromJson(result, node, "")
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))
when false: when false:
import os import os

View file

@ -1143,6 +1143,15 @@ proc recv*(socket: Socket, data: var string, size: int, timeout = -1,
flags = {SocketFlag.SafeDisconn}): int = flags = {SocketFlag.SafeDisconn}): int =
## Higher-level version of ``recv``. ## 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. ## When 0 is returned the socket's connection has been closed.
## ##
## This function will throw an OSError exception when an error occurs. A value ## 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.} = flags = {SocketFlag.SafeDisconn}): string {.inline.} =
## Higher-level version of ``recv`` which returns a string. ## 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. ## When ``""`` is returned the socket's connection has been closed.
## ##
## This function will throw an OSError exception when an error occurs. ## This function will throw an OSError exception when an error occurs.

View file

@ -15,18 +15,18 @@
## import os ## import os
## ##
## let myFile = "/path/to/my/file.nim" ## let myFile = "/path/to/my/file.nim"
## ##
## let pathSplit = splitPath(myFile) ## let pathSplit = splitPath(myFile)
## assert pathSplit.head == "/path/to/my" ## assert pathSplit.head == "/path/to/my"
## assert pathSplit.tail == "file.nim" ## assert pathSplit.tail == "file.nim"
## ##
## assert parentDir(myFile) == "/path/to/my" ## assert parentDir(myFile) == "/path/to/my"
## ##
## let fileSplit = splitFile(myFile) ## let fileSplit = splitFile(myFile)
## assert fileSplit.dir == "/path/to/my" ## assert fileSplit.dir == "/path/to/my"
## assert fileSplit.name == "file" ## assert fileSplit.name == "file"
## assert fileSplit.ext == ".nim" ## assert fileSplit.ext == ".nim"
## ##
## assert myFile.changeFileExt("c") == "/path/to/my/file.c" ## assert myFile.changeFileExt("c") == "/path/to/my/file.c"
## ##
@ -232,11 +232,92 @@ proc splitPath*(path: string): tuple[head, tail: string] {.
result.head = "" result.head = ""
result.tail = path 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: when FileSystemCaseSensitive:
template `!=?`(a, b: char): bool = a != b template `!=?`(a, b: char): bool = a != b
else: else:
template `!=?`(a, b: char): bool = toLowerAscii(a) != toLowerAscii(b) 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 {. proc relativePath*(path, base: string; sep = DirSep): string {.
noSideEffect, rtl, extern: "nos$1", raises: [].} = noSideEffect, rtl, extern: "nos$1", raises: [].} =
## Converts `path` to a path relative to `base`. ## 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 `'/'` ## this can be useful to ensure the relative path only contains `'/'`
## so that it can be used for URL constructions. ## 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: ## See also:
## * `splitPath proc <#splitPath,string>`_ ## * `splitPath proc <#splitPath,string>`_
## * `parentDir proc <#parentDir,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/me/bar/z.nim", "/Users/me", '/') == "bar/z.nim"
assert relativePath("", "/users/moo", '/') == "" 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 "" 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 f, b: PathIter
var ff = (0, -1) var ff = (0, -1)
var bb = (0, -1) # (int, int) var bb = (0, -1) # (int, int)
@ -645,32 +734,6 @@ proc cmpPaths*(pathA, pathB: string): int {.
else: else:
result = cmpIgnoreCase(a, b) 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 {. proc unixToNativePath*(path: string, drive=""): string {.
noSideEffect, rtl, extern: "nos$1".} = noSideEffect, rtl, extern: "nos$1".} =
## Converts an UNIX-like path to a native one. ## 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) len = readlink(procPath, result, len)
setLen(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): when not (defined(windows) or defined(macosx) or weirdTarget):
proc getApplHeuristic(): string = proc getApplHeuristic(): string =
when declared(paramStr): when declared(paramStr):
@ -2761,6 +2865,9 @@ proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect], noN
result = getApplFreebsd() result = getApplFreebsd()
elif defined(haiku): elif defined(haiku):
result = getApplHaiku() result = getApplHaiku()
elif defined(openbsd):
result = getApplOpenBsd()
# little heuristic that may work on other POSIX-like systems: # little heuristic that may work on other POSIX-like systems:
if result.len == 0: if result.len == 0:
result = getApplHeuristic() result = getApplHeuristic()

View file

@ -561,7 +561,7 @@ proc writeConfig*(dict: Config, filename: string) =
dict.writeConfig(fileStream) dict.writeConfig(fileStream)
proc getSectionValue*(dict: Config, section, key: string): string = 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.hasKey(section):
if dict[section].hasKey(key): if dict[section].hasKey(key):
result = dict[section][key] result = dict[section][key]

View file

@ -84,11 +84,11 @@ include "system/inclrtl"
{.push debugger: off.} {.push debugger: off.}
when defined(JS): when defined(JS):
type ui = uint32 type Ui = uint32
const randMax = 4_294_967_295u32 const randMax = 4_294_967_295u32
else: else:
type ui = uint64 type Ui = uint64
const randMax = 18_446_744_073_709_551_615u64 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 ## 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 ## another that uses the default generator. The procs that use the default
## generator are **not** thread-safe! ## generator are **not** thread-safe!
a0, a1: ui a0, a1: Ui
when defined(JS): when defined(JS):
var state = Rand( var state = Rand(
@ -118,8 +118,8 @@ else:
a0: 0x69B4C98CB8530805u64, a0: 0x69B4C98CB8530805u64,
a1: 0xFED1DD3004688D67CAu64) # global for backwards compatibility a1: 0xFED1DD3004688D67CAu64) # global for backwards compatibility
proc rotl(x, k: ui): ui = proc rotl(x, k: Ui): Ui =
result = (x shl k) or (x shr (ui(64) - k)) result = (x shl k) or (x shr (Ui(64) - k))
proc next*(r: var Rand): uint64 = proc next*(r: var Rand): uint64 =
## Computes a random ``uint64`` number using the given state. ## Computes a random ``uint64`` number using the given state.
@ -195,11 +195,11 @@ proc skipRandomNumbers*(s: var Rand) =
else: else:
const helper = [0xbeac0467eba5facbu64, 0xd86b048b86aa9922u64] const helper = [0xbeac0467eba5facbu64, 0xd86b048b86aa9922u64]
var var
s0 = ui 0 s0 = Ui 0
s1 = ui 0 s1 = Ui 0
for i in 0..high(helper): for i in 0..high(helper):
for b in 0 ..< 64: 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 s0 = s0 xor s.a0
s1 = s1 xor s.a1 s1 = s1 xor s.a1
discard next(s) discard next(s)
@ -210,7 +210,7 @@ proc random*(max: int): int {.benign, deprecated:
"Deprecated since v0.18.0; use 'rand' instead".} = "Deprecated since v0.18.0; use 'rand' instead".} =
while true: while true:
let x = next(state) let x = next(state)
if x < randMax - (randMax mod ui(max)): if x < randMax - (randMax mod Ui(max)):
return int(x mod uint64(max)) return int(x mod uint64(max))
proc random*(max: float): float {.benign, deprecated: proc random*(max: float): float {.benign, deprecated:
@ -247,7 +247,7 @@ proc rand*(r: var Rand; max: Natural): int {.benign.} =
if max == 0: return if max == 0: return
while true: while true:
let x = next(r) 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)) return int(x mod (uint64(max)+1u64))
proc rand*(max: int): int {.benign.} = proc rand*(max: int): int {.benign.} =
@ -570,8 +570,8 @@ proc initRand*(seed: int64): Rand =
let now = getTime() let now = getTime()
var r2 = initRand(now.toUnix * 1_000_000_000 + now.nanosecond) var r2 = initRand(now.toUnix * 1_000_000_000 + now.nanosecond)
doAssert seed != 0 # 0 causes `rand(int)` to always return 0 for example. doAssert seed != 0 # 0 causes `rand(int)` to always return 0 for example.
result.a0 = ui(seed shr 16) result.a0 = Ui(seed shr 16)
result.a1 = ui(seed and 0xffff) result.a1 = Ui(seed and 0xffff)
discard next(result) discard next(result)
proc randomize*(seed: int64) {.benign.} = proc randomize*(seed: int64) {.benign.} =
@ -642,7 +642,7 @@ when not defined(nimscript):
## * `randomize proc<#randomize,int64>`_ that accepts a seed ## * `randomize proc<#randomize,int64>`_ that accepts a seed
## * `initRand proc<#initRand,int64>`_ ## * `initRand proc<#initRand,int64>`_
when defined(JS): when defined(JS):
let time = int64(times.epochTime() * 1000) let time = int64(times.epochTime() * 1000) and 0x7fff_ffff
randomize(time) randomize(time)
else: else:
let now = times.getTime() let now = times.getTime()

View file

@ -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 ``${}`` extensible with ordinary Nim procs. The proc is either enclosed in ``${}``
or in ``$[]``. ``${}`` matches and binds the result or in ``$[]``. ``${}`` matches and binds the result
to a variable (that was passed to the ``scanf`` macro) while ``$[]`` merely 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 In this example, we define a helper proc ``someSep`` that skips some separators

View file

@ -180,7 +180,6 @@ macro `[]`*(lc: ListComprehension, comp, typ: untyped): untyped {.deprecated.} =
newNimNode(nnkBracket))), newNimNode(nnkBracket))),
result)))) result))))
macro dump*(x: typed): untyped = macro dump*(x: typed): untyped =
## Dumps the content of an expression, useful for debugging. ## Dumps the content of an expression, useful for debugging.
## It accepts any expression and prints a textual representation ## It accepts any expression and prints a textual representation

View file

@ -186,7 +186,7 @@ proc delOutputFormatter*(formatter: OutputFormatter) =
keepIf(formatters, proc (x: OutputFormatter): bool = keepIf(formatters, proc (x: OutputFormatter): bool =
x != formatter) x != formatter)
proc newConsoleOutputFormatter*(outputLevel: OutputLevel = PRINT_ALL, proc newConsoleOutputFormatter*(outputLevel: OutputLevel = OutputLevel.PRINT_ALL,
colorOutput = true): <//>ConsoleOutputFormatter = colorOutput = true): <//>ConsoleOutputFormatter =
ConsoleOutputFormatter( ConsoleOutputFormatter(
outputLevel: outputLevel, outputLevel: outputLevel,
@ -207,7 +207,7 @@ proc defaultConsoleFormatter*(): <//>ConsoleOutputFormatter =
colorOutput = true colorOutput = true
elif existsEnv("NIMTEST_NO_COLOR"): elif existsEnv("NIMTEST_NO_COLOR"):
colorOutput = false colorOutput = false
var outputLevel = PRINT_ALL var outputLevel = OutputLevel.PRINT_ALL
if envOutLvl.len > 0: if envOutLvl.len > 0:
for opt in countup(low(OutputLevel), high(OutputLevel)): for opt in countup(low(OutputLevel), high(OutputLevel)):
if $opt == envOutLvl: if $opt == envOutLvl:
@ -240,17 +240,17 @@ method failureOccurred*(formatter: ConsoleOutputFormatter,
method testEnded*(formatter: ConsoleOutputFormatter, testResult: TestResult) = method testEnded*(formatter: ConsoleOutputFormatter, testResult: TestResult) =
formatter.isInTest = false formatter.isInTest = false
if formatter.outputLevel != PRINT_NONE and if formatter.outputLevel != OutputLevel.PRINT_NONE and
(formatter.outputLevel == PRINT_ALL or testResult.status == FAILED): (formatter.outputLevel == OutputLevel.PRINT_ALL or testResult.status == TestStatus.FAILED):
let prefix = if testResult.suiteName.len > 0: " " else: "" let prefix = if testResult.suiteName.len > 0: " " else: ""
template rawPrint() = echo(prefix, "[", $testResult.status, "] ", template rawPrint() = echo(prefix, "[", $testResult.status, "] ",
testResult.testName) testResult.testName)
when not defined(ECMAScript): when not defined(ECMAScript):
if formatter.colorOutput and not defined(ECMAScript): if formatter.colorOutput and not defined(ECMAScript):
var color = case testResult.status var color = case testResult.status
of OK: fgGreen of TestStatus.OK: fgGreen
of FAILED: fgRed of TestStatus.FAILED: fgRed
of SKIPPED: fgYellow of TestStatus.SKIPPED: fgYellow
styledEcho styleBright, color, prefix, "[", $testResult.status, "] ", styledEcho styleBright, color, prefix, "[", $testResult.status, "] ",
resetStyle, testResult.testName resetStyle, testResult.testName
else: else:
@ -318,11 +318,11 @@ method testEnded*(formatter: JUnitOutputFormatter, testResult: TestResult) =
formatter.stream.writeLine("\t\t<testcase name=\"$#\" time=\"$#\">" % [ formatter.stream.writeLine("\t\t<testcase name=\"$#\" time=\"$#\">" % [
xmlEscape(testResult.testName), timeStr]) xmlEscape(testResult.testName), timeStr])
case testResult.status case testResult.status
of OK: of TestStatus.OK:
discard discard
of SKIPPED: of TestStatus.SKIPPED:
formatter.stream.writeLine("<skipped />") formatter.stream.writeLine("<skipped />")
of FAILED: of TestStatus.FAILED:
let failureMsg = if formatter.testStackTrace.len > 0 and let failureMsg = if formatter.testStackTrace.len > 0 and
formatter.testErrors.len > 0: formatter.testErrors.len > 0:
xmlEscape(formatter.testErrors[^1]) xmlEscape(formatter.testErrors[^1])
@ -498,7 +498,7 @@ template test*(name, body) {.dirty.} =
if shouldRun(when declared(testSuiteName): testSuiteName else: "", name): if shouldRun(when declared(testSuiteName): testSuiteName else: "", name):
checkpoints = @[] checkpoints = @[]
var testStatusIMPL {.inject.} = OK var testStatusIMPL {.inject.} = TestStatus.OK
for formatter in formatters: for formatter in formatters:
formatter.testStarted(name) formatter.testStarted(name)
@ -518,7 +518,7 @@ template test*(name, body) {.dirty.} =
fail() fail()
finally: finally:
if testStatusIMPL == FAILED: if testStatusIMPL == TestStatus.FAILED:
programResult = 1 programResult = 1
let testResult = TestResult( let testResult = TestResult(
suiteName: when declared(testSuiteName): testSuiteName else: "", suiteName: when declared(testSuiteName): testSuiteName else: "",
@ -558,7 +558,7 @@ template fail* =
bind ensureInitialized bind ensureInitialized
when declared(testStatusIMPL): when declared(testStatusIMPL):
testStatusIMPL = FAILED testStatusIMPL = TestStatus.FAILED
else: else:
programResult = 1 programResult = 1
@ -589,7 +589,7 @@ template skip* =
## skip() ## skip()
bind checkpoints bind checkpoints
testStatusIMPL = SKIPPED testStatusIMPL = TestStatus.SKIPPED
checkpoints = @[] checkpoints = @[]
macro check*(conditions: untyped): untyped = macro check*(conditions: untyped): untyped =

View file

@ -671,7 +671,7 @@ include "system/inclrtl"
const NoFakeVars* = defined(nimscript) ## `true` if the backend doesn't support \ const NoFakeVars* = defined(nimscript) ## `true` if the backend doesn't support \
## "fake variables" like `var EBADF {.importc.}: cint`. ## "fake variables" like `var EBADF {.importc.}: cint`.
when not defined(JS) and not defined(gcDestructors): when not defined(JS) and not defined(nimSeqsV2):
type type
TGenericSeq {.compilerproc, pure, inheritable.} = object TGenericSeq {.compilerproc, pure, inheritable.} = object
len, reserved: int len, reserved: int
@ -684,7 +684,7 @@ when not defined(JS) and not defined(gcDestructors):
NimString = ptr NimStringDesc NimString = ptr NimStringDesc
when not defined(JS) and not defined(nimscript): when not defined(JS) and not defined(nimscript):
when not defined(gcDestructors): when not defined(nimSeqsV2):
template space(s: PGenericSeq): int {.dirty.} = template space(s: PGenericSeq): int {.dirty.} =
s.reserved and not (seqShallowFlag or strlitFlag) s.reserved and not (seqShallowFlag or strlitFlag)
when not defined(nimV2): when not defined(nimV2):
@ -1020,7 +1020,7 @@ when not defined(JS):
## assert len(x) == 3 ## assert len(x) == 3
## x[0] = 10 ## x[0] = 10
result = newSeqOfCap[T](len) result = newSeqOfCap[T](len)
when defined(gcDestructors): when defined(nimSeqsV2):
cast[ptr int](addr result)[] = len cast[ptr int](addr result)[] = len
else: else:
var s = cast[PGenericSeq](result) 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: int32): int32 {.magic: "ModI", noSideEffect.}
proc `mod`*(x, y: int64): int64 {.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."
when defined(nimOldShiftRight) or not defined(nimAshr): proc `shr`*(x: int, y: SomeInteger): int {.magic: "ShrI", noSideEffect, deprecated: shrDepMessage.}
const shrDepMessage = "`shr` will become sign preserving." proc `shr`*(x: int8, y: SomeInteger): int8 {.magic: "ShrI", noSideEffect, deprecated: shrDepMessage.}
proc `shr`*(x: int, y: SomeInteger): int {.magic: "ShrI", noSideEffect, deprecated: shrDepMessage.} proc `shr`*(x: int16, y: SomeInteger): int16 {.magic: "ShrI", noSideEffect, deprecated: shrDepMessage.}
proc `shr`*(x: int8, y: SomeInteger): int8 {.magic: "ShrI", noSideEffect, deprecated: shrDepMessage.} proc `shr`*(x: int32, y: SomeInteger): int32 {.magic: "ShrI", noSideEffect, deprecated: shrDepMessage.}
proc `shr`*(x: int16, y: SomeInteger): int16 {.magic: "ShrI", noSideEffect, deprecated: shrDepMessage.} proc `shr`*(x: int64, y: SomeInteger): int64 {.magic: "ShrI", noSideEffect, deprecated: shrDepMessage.}
proc `shr`*(x: int32, y: SomeInteger): int32 {.magic: "ShrI", noSideEffect, deprecated: shrDepMessage.} else:
proc `shr`*(x: int64, y: SomeInteger): int64 {.magic: "ShrI", noSideEffect, deprecated: shrDepMessage.} proc `shr`*(x: int, y: SomeInteger): int {.magic: "AshrI", noSideEffect.}
else: ## Computes the `shift right` operation of `x` and `y`, filling
proc `shr`*(x: int, y: SomeInteger): int {.magic: "AshrI", noSideEffect.} ## vacant bit positions with the sign bit.
## 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`.
## ##
## **Note**: `Operator precedence <manual.html#syntax-precedence>`_ ## **Note**: `Operator precedence <manual.html#syntax-precedence>`_
## is different than in *C*. ## is different than in *C*.
## ##
## See also:
## * `ashr proc <#ashr,int,SomeInteger>`_ for arithmetic shift right
##
## .. code-block:: Nim ## .. code-block:: Nim
## 1'i32 shl 4 == 0x0000_0010 ## 0b0001_0000'i8 shr 2 == 0b0000_0100'i8
## 1'i64 shl 4 == 0x0000_0000_0000_0010 ## 0b0000_0001'i8 shr 1 == 0b0000_0000'i8
proc `shl`*(x: int8, y: SomeInteger): int8 {.magic: "ShlI", noSideEffect.} ## 0b1000_0000'i8 shr 4 == 0b1111_1000'i8
proc `shl`*(x: int16, y: SomeInteger): int16 {.magic: "ShlI", noSideEffect.} ## -1 shr 5 == -1
proc `shl`*(x: int32, y: SomeInteger): int32 {.magic: "ShlI", noSideEffect.} ## 1 shr 5 == 0
proc `shl`*(x: int64, y: SomeInteger): int64 {.magic: "ShlI", noSideEffect.} ## 16 shr 2 == 4
else: ## -16 shr 2 == -4
proc `shr`*(x, y: int): int {.magic: "ShrI", noSideEffect.} proc `shr`*(x: int8, y: SomeInteger): int8 {.magic: "AshrI", noSideEffect.}
proc `shr`*(x, y: int8): int8 {.magic: "ShrI", noSideEffect.} proc `shr`*(x: int16, y: SomeInteger): int16 {.magic: "AshrI", noSideEffect.}
proc `shr`*(x, y: int16): int16 {.magic: "ShrI", noSideEffect.} proc `shr`*(x: int32, y: SomeInteger): int32 {.magic: "AshrI", noSideEffect.}
proc `shr`*(x, y: int32): int32 {.magic: "ShrI", noSideEffect.} proc `shr`*(x: int64, y: SomeInteger): int64 {.magic: "AshrI", noSideEffect.}
proc `shr`*(x, y: int64): int64 {.magic: "ShrI", noSideEffect.}
proc `shl`*(x, y: int): int {.magic: "ShlI", noSideEffect.}
proc `shl`*(x, y: int8): int8 {.magic: "ShlI", noSideEffect.} proc `shl`*(x: int, y: SomeInteger): int {.magic: "ShlI", noSideEffect.}
proc `shl`*(x, y: int16): int16 {.magic: "ShlI", noSideEffect.} ## Computes the `shift left` operation of `x` and `y`.
proc `shl`*(x, y: int32): int32 {.magic: "ShlI", noSideEffect.} ##
proc `shl`*(x, y: int64): int64 {.magic: "ShlI", noSideEffect.} ## **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): when defined(nimAshr):
proc ashr*(x: int, y: SomeInteger): int {.magic: "AshrI", noSideEffect.} proc ashr*(x: int, y: SomeInteger): int {.magic: "AshrI", noSideEffect.}
@ -1518,54 +1504,105 @@ template `>%`*(x, y: untyped): untyped = y <% x
# unsigned integer operations: # 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`. ## 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`*(x: uint, y: SomeInteger): uint {.magic: "ShrI", noSideEffect.}
proc `shr`*[T: SomeUnsignedInt](x: T, y: SomeInteger): T {.magic: "ShrI", noSideEffect.} ## Computes the `shift right` operation of `x` and `y`.
## Computes the `shift right` operation of `x` and `y`. proc `shr`*(x: uint8, y: SomeInteger): uint8 {.magic: "ShrI", noSideEffect.}
proc `shl`*[T: SomeUnsignedInt](x: T, y: SomeInteger): T {.magic: "ShlI", noSideEffect.} proc `shr`*(x: uint16, y: SomeInteger): uint16 {.magic: "ShrI", noSideEffect.}
## Computes the `shift left` operation of `x` and `y`. proc `shr`*(x: uint32, y: SomeInteger): uint32 {.magic: "ShrI", noSideEffect.}
else: proc `shr`*(x: uint64, y: SomeInteger): uint64 {.magic: "ShrI", noSideEffect.}
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 `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`. ## 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`. ## 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`. ## 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. ## 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. ## 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. ## 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. ## 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. ## Computes the integer division for unsigned integers.
## This is roughly the same as ``trunc(x/y)``. ## 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. ## Computes the integer modulo operation (remainder) for unsigned integers.
## This is the same as ``x - (x div y) * y``. ## 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``. ## 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)``. ## 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: # floating point operations:
proc `+`*(x: float32): float32 {.magic: "UnaryPlusF64", noSideEffect.} 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": when not defined(JS) and not defined(nimscript) and hostOS != "standalone":
include "system/cgprocs" 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.} 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.} proc add*[T](x: var seq[T], y: T) {.magic: "AppendSeqElem", noSideEffect.}
## Generic proc for adding a data item `y` to a container `x`. ## 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) setLen(x, xl + y.len)
for i in 0..high(y): x[xl+i] = y[i] for i in 0..high(y): x[xl+i] = y[i]
when defined(gcDestructors): when defined(nimSeqsV2):
template movingCopy(a, b) = template movingCopy(a, b) =
a = move(b) a = move(b)
else: else:
@ -3002,7 +3039,7 @@ proc `==`*[T](x, y: seq[T]): bool {.noSideEffect.} =
else: else:
when not defined(JS): when not defined(JS):
proc seqToPtr[T](x: seq[T]): pointer {.inline, noSideEffect.} = proc seqToPtr[T](x: seq[T]): pointer {.inline, noSideEffect.} =
when defined(gcDestructors): when defined(nimSeqsV2):
result = cast[NimSeqV2[T]](x).p result = cast[NimSeqV2[T]](x).p
else: else:
result = cast[pointer](x) result = cast[pointer](x)
@ -3098,7 +3135,7 @@ when not defined(js):
name: cstring name: cstring
PNimType = ptr TNimType PNimType = ptr TNimType
when defined(gcDestructors) and not defined(nimscript): when defined(nimSeqsV2) and not defined(nimscript):
include "core/strs" include "core/strs"
include "core/seqs" include "core/seqs"
@ -3747,7 +3784,7 @@ when not defined(JS): #and not defined(nimscript):
{.pop.} {.pop.}
{.push stack_trace: off, profiler:off.} {.push stack_trace: off, profiler:off.}
when hasAlloc: when hasAlloc:
when not defined(gcDestructors): when not defined(nimSeqsV2):
include "system/sysstr" include "system/sysstr"
{.pop.} {.pop.}
when hasAlloc: include "system/strmantle" when hasAlloc: include "system/strmantle"
@ -4162,7 +4199,7 @@ proc shallow*(s: var string) {.noSideEffect, inline.} =
## perform deep copies of `s`. ## perform deep copies of `s`.
## ##
## This is only useful for optimization purposes. ## 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) var s = cast[PGenericSeq](s)
if s == nil: if s == nil:
s = cast[PGenericSeq](newString(0)) s = cast[PGenericSeq](newString(0))

View file

@ -439,7 +439,7 @@ proc getStackTrace(e: ref Exception): string =
proc getStackTraceEntries*(e: ref Exception): seq[StackTraceEntry] = proc getStackTraceEntries*(e: ref Exception): seq[StackTraceEntry] =
## Returns the attached stack trace to the exception ``e`` as ## Returns the attached stack trace to the exception ``e`` as
## a ``seq``. This is not yet available for the JS backend. ## a ``seq``. This is not yet available for the JS backend.
when not defined(gcDestructors): when not defined(nimSeqsV2):
shallowCopy(result, e.trace) shallowCopy(result, e.trace)
else: else:
result = move(e.trace) result = move(e.trace)

View file

@ -254,7 +254,7 @@ proc forAllChildren(cell: PCell, op: WalkOp) =
of tyRef: # common case of tyRef: # common case
forAllChildrenAux(cellToUsr(cell), cell.typ.base, op) forAllChildrenAux(cellToUsr(cell), cell.typ.base, op)
of tySequence: of tySequence:
when not defined(gcDestructors): when not defined(nimSeqsV2):
var d = cast[ByteAddress](cellToUsr(cell)) var d = cast[ByteAddress](cellToUsr(cell))
var s = cast[PGenericSeq](d) var s = cast[PGenericSeq](d)
if s != nil: if s != nil:
@ -304,7 +304,7 @@ proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} =
zeroMem(result, size) zeroMem(result, size)
when defined(memProfiler): nimProfile(size) when defined(memProfiler): nimProfile(size)
when not defined(gcDestructors): when not defined(nimSeqsV2):
proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} = proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} =
# `newObj` already uses locks, so no need for them here. # `newObj` already uses locks, so no need for them here.
let size = addInt(mulInt(len, typ.base.size), GenericSeqSize) let size = addInt(mulInt(len, typ.base.size), GenericSeqSize)

View file

@ -112,6 +112,8 @@ proc c_setvbuf(f: File, buf: pointer, mode: cint, size: csize): cint {.
proc c_fprintf(f: File, frmt: cstring): cint {. proc c_fprintf(f: File, frmt: cstring): cint {.
importc: "fprintf", header: "<stdio.h>", varargs, discardable.} 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 ## When running nim in android app stdout goes no where, so echo gets ignored
## To redreict echo to the android logcat use -d:androidNDK ## To redreict echo to the android logcat use -d:androidNDK
@ -212,6 +214,10 @@ when defined(windows):
var i = c_fprintf(f, "%s", s) var i = c_fprintf(f, "%s", s)
while i < s.len: while i < s.len:
if s[i] == '\0': if s[i] == '\0':
let w = c_fputc('\0', f)
if w != 0:
if doRaise: raiseEIO("cannot write string to file")
break
inc i inc i
else: else:
let w = c_fprintf(f, "%s", unsafeAddr s[i]) 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.} = proc readFile*(filename: string): TaintedString {.tags: [ReadIOEffect], benign.} =
## Opens a file named `filename` for reading, calls `readAll ## Opens a file named `filename` for reading, calls `readAll
## <#readAll>`_ and closes the file afterwards. Returns the string. ## <#readAll,File>`_ and closes the file afterwards. Returns the string.
## Raises an IO exception in case of an error. If # you need to call ## Raises an IO exception in case of an error. If you need to call
## this inside a compile time macro you can use `staticRead ## this inside a compile time macro you can use `staticRead
## <#staticRead>`_. ## <system.html#staticRead,string>`_.
var f: File var f: File
if open(f, filename): if open(f, filename):
try: try:

View file

@ -152,7 +152,9 @@ when defined(boehmgc):
proc nimGC_setStackBottom(theStackBottom: pointer) = discard proc nimGC_setStackBottom(theStackBottom: pointer) = discard
proc initGC() = proc initGC() =
boehmGC_set_all_interior_pointers(0) when defined(boehmNoIntPtr):
# See #12286
boehmGC_set_all_interior_pointers(0)
boehmGCinit() boehmGCinit()
when hasThreadSupport: when hasThreadSupport:
boehmGC_allow_register_threads() boehmGC_allow_register_threads()
@ -516,7 +518,7 @@ else:
else: else:
include "system/gc" 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.} = proc nimNewSeqOfCap(typ: PNimType, cap: int): pointer {.compilerproc.} =
when defined(gcRegions): when defined(gcRegions):
let s = mulInt(cap, typ.base.size) # newStr already adds GenericSeqSize let s = mulInt(cap, typ.base.size) # newStr already adds GenericSeqSize

View file

@ -160,7 +160,7 @@ when not defined(useNimRtl):
reprAux(result, cast[pointer](cast[ByteAddress](p) + i*bs), typ.base, cl) reprAux(result, cast[pointer](cast[ByteAddress](p) + i*bs), typ.base, cl)
add result, "]" add result, "]"
when defined(gcDestructors): when defined(nimSeqsV2):
type type
GenericSeq = object GenericSeq = object
len: int len: int

View file

@ -849,6 +849,20 @@ function main() {
</ul> </ul>
</li> </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&apos;f64"><wbr />C_<wbr />A<span class="attachedType"></span></a></li>
<li><a class="reference" href="#C_B"
title="C_B = 0o377&apos;i8"><wbr />C_<wbr />B<span class="attachedType"></span></a></li>
<li><a class="reference" href="#C_C"
title="C_C = 0o277&apos;i8"><wbr />C_<wbr />C<span class="attachedType"></span></a></li>
<li><a class="reference" href="#C_D"
title="C_D = 0o177777&apos;i16"><wbr />C_<wbr />D<span class="attachedType"></span></a></li>
</ul>
</li>
<li> <li>
<a class="reference reference-toplevel" href="#12" id="62">Procs</a> <a class="reference reference-toplevel" href="#12" id="62">Procs</a>
<ul class="simple simple-toc-section"> <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> </dd>
</dl></div> </dl></div>

View file

@ -840,6 +840,22 @@ function main() {
<li><a class="reference external" <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> 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> </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"> <dt><a name="enumValueA" href="#enumValueA"><span>enumValueA:</span></a></dt><dd><ul class="simple">
<li><a class="reference external" <li><a class="reference external"
data-doc-search-tag="SomeType.enumValueA" href="subdir/subdir_b/utils.html#enumValueA">SomeType.enumValueA</a></li> data-doc-search-tag="SomeType.enumValueA" href="subdir/subdir_b/utils.html#enumValueA">SomeType.enumValueA</a></li>

View file

@ -9,6 +9,11 @@ runnableExamples:
# bug #11078 # bug #11078
for x in "xx": discard 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) = template foo*(a, b: SomeType) =
## This does nothing ## This does nothing
@ -31,7 +36,7 @@ import std/macros
macro bar*(): untyped = macro bar*(): untyped =
result = newStmtList() result = newStmtList()
var aVariable*: array[1,int] var aVariable*: array[1, int]
aEnum() aEnum()
bEnum() bEnum()

View file

@ -48,6 +48,7 @@ Options:
--maxresults:N limit the number of suggestions to N --maxresults:N limit the number of suggestions to N
--tester implies --stdin and outputs a line --tester implies --stdin and outputs a line
'""" & DummyEof & """' for the tester '""" & 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. The server then listens to the connection and takes line-based commands.
@ -92,7 +93,7 @@ proc myLog(s: string) =
const const
seps = {':', ';', ' ', '\t'} 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 'quit' to quit\n" &
"type 'debug' to toggle debug mode on/off\n" & "type 'debug' to toggle debug mode on/off\n" &
"type 'terse' to toggle terse mode on/off" "type 'terse' to toggle terse mode on/off"
@ -244,6 +245,7 @@ proc toStdout() {.gcsafe.} =
of ideNone: break of ideNone: break
of ideMsg: echo res.doc of ideMsg: echo res.doc
of ideKnown: echo res.quality == 1 of ideKnown: echo res.quality == 1
of ideProject: echo res.filePath
else: echo res else: echo res
proc toSocket(stdoutSocket: Socket) {.gcsafe.} = proc toSocket(stdoutSocket: Socket) {.gcsafe.} =
@ -253,6 +255,7 @@ proc toSocket(stdoutSocket: Socket) {.gcsafe.} =
of ideNone: break of ideNone: break
of ideMsg: stdoutSocket.send(res.doc & "\c\L") of ideMsg: stdoutSocket.send(res.doc & "\c\L")
of ideKnown: stdoutSocket.send($(res.quality == 1) & "\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") else: stdoutSocket.send($res & "\c\L")
proc toEpc(client: Socket; uid: BiggestInt) {.gcsafe.} = proc toEpc(client: Socket; uid: BiggestInt) {.gcsafe.} =
@ -265,6 +268,8 @@ proc toEpc(client: Socket; uid: BiggestInt) {.gcsafe.} =
list.add sexp(res.doc) list.add sexp(res.doc)
of ideKnown: of ideKnown:
list.add sexp(res.quality == 1) list.add sexp(res.quality == 1)
of ideProject:
list.add sexp(res.filePath)
else: else:
list.add sexp(res) list.add sexp(res)
returnEpc(client, uid, list) returnEpc(client, uid, list)
@ -319,8 +324,8 @@ proc replTcp(x: ThreadParams) {.thread.} =
server.bindAddr(x.port, x.address) server.bindAddr(x.port, x.address)
server.listen() server.listen()
var inp = "".TaintedString var inp = "".TaintedString
var stdoutSocket: Socket
while true: while true:
var stdoutSocket = newSocket()
accept(server, stdoutSocket) accept(server, stdoutSocket)
stdoutSocket.readLine(inp) stdoutSocket.readLine(inp)
@ -353,7 +358,7 @@ proc replEpc(x: ThreadParams) {.thread.} =
echo port echo port
stdout.flushFile() stdout.flushFile()
var client = newSocket() var client: Socket
# Wait for connection # Wait for connection
accept(server, client) accept(server, client)
while true: while true:
@ -434,6 +439,7 @@ proc execCmd(cmd: string; graph: ModuleGraph; cachedMsgs: CachedMsgs) =
of "debug": toggle optIdeDebug of "debug": toggle optIdeDebug
of "terse": toggle optIdeTerse of "terse": toggle optIdeTerse
of "known": conf.ideCmd = ideKnown of "known": conf.ideCmd = ideKnown
of "project": conf.ideCmd = ideProject
else: err() else: err()
var dirtyfile = "" var dirtyfile = ""
var orig = "" var orig = ""
@ -453,6 +459,8 @@ proc execCmd(cmd: string; graph: ModuleGraph; cachedMsgs: CachedMsgs) =
if conf.ideCmd == ideKnown: if conf.ideCmd == ideKnown:
results.send(Suggest(section: ideKnown, quality: ord(fileInfoKnown(conf, AbsoluteFile orig)))) 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: else:
if conf.ideCmd == ideChk: if conf.ideCmd == ideChk:
for cm in cachedMsgs: errorHook(conf, cm.info, cm.msg, cm.sev) 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) = proc processCmdLine*(pass: TCmdLinePass, cmd: string; conf: ConfigRef) =
var p = parseopt.initOptParser(cmd) var p = parseopt.initOptParser(cmd)
var findProject = false
while true: while true:
parseopt.next(p) parseopt.next(p)
case p.kind case p.kind
@ -594,6 +603,8 @@ proc processCmdLine*(pass: TCmdLinePass, cmd: string; conf: ConfigRef) =
gRefresh = true gRefresh = true
of "maxresults": of "maxresults":
conf.suggestMaxResults = parseInt(p.val) conf.suggestMaxResults = parseInt(p.val)
of "find":
findProject = true
else: processSwitch(pass, p, conf) else: processSwitch(pass, p, conf)
of cmdArgument: of cmdArgument:
let a = unixToNativePath(p.key) 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: # don't make it worse, report the error the old way:
if conf.projectName.len == 0: conf.projectName = a if conf.projectName.len == 0: conf.projectName = a
else: 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 # if processArgument(pass, p, argsCount): break
proc handleCmdLine(cache: IdentCache; conf: ConfigRef) = proc handleCmdLine(cache: IdentCache; conf: ConfigRef) =
@ -732,6 +748,8 @@ else:
stderr.write s & "\n" stderr.write s & "\n"
if conf.ideCmd == ideKnown: if conf.ideCmd == ideKnown:
retval.add(Suggest(section: ideKnown, quality: ord(fileInfoKnown(conf, file)))) retval.add(Suggest(section: ideKnown, quality: ord(fileInfoKnown(conf, file))))
elif conf.ideCmd == ideProject:
retval.add(Suggest(section: ideProject, filePath: string conf.projectFull))
else: else:
if conf.ideCmd == ideChk: if conf.ideCmd == ideChk:
for cm in nimsuggest.cachedMsgs: errorHook(conf, cm.info, cm.msg, cm.sev) for cm in nimsuggest.cachedMsgs: errorHook(conf, cm.info, cm.msg, cm.sev)

View file

@ -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). 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 git clone https://github.com/nim-lang/Nim.git
cd Nim 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 Finally, once you have finished the build steps (on Windows, Mac or Linux) you
should add the ``bin`` directory to your PATH. should add the ``bin`` directory to your PATH.

View file

@ -37,8 +37,8 @@ pkg "gnuplot"
pkg "hts", "nim c -o:htss -r src/hts.nim" pkg "hts", "nim c -o:htss -r src/hts.nim"
pkg "illwill", "nimble examples" pkg "illwill", "nimble examples"
pkg "inim" pkg "inim"
pkg "itertools", "nim doc src/itertools.nim" # pkg "itertools", "nim doc src/itertools.nim"
pkg "iterutils" # pkg "iterutils"
pkg "jstin" pkg "jstin"
pkg "karax", "nim c -r tests/tester.nim" pkg "karax", "nim c -r tests/tester.nim"
pkg "loopfusion" pkg "loopfusion"

View file

@ -4,6 +4,14 @@ int32
int32 int32
1280 1280
1280 1280
3
1
2
2
3
4294967295
2
0
''' '''
""" """
@ -145,3 +153,17 @@ block tsubrange:
level = min(level + 2, maxLevel).n16 level = min(level + 2, maxLevel).n16
doAssert level == 1 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)

View file

@ -582,3 +582,11 @@ template append2*(args: varargs[string, myAppend]): string =
let foo = append2("1", "2", "3") let foo = append2("1", "2", "3")
echo foo 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

View file

@ -43,4 +43,14 @@ block: #8399
waitFor foo("$asd") 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" echo "ok"

View file

@ -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)

View 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)

View file

@ -233,7 +233,7 @@ block tablesref:
for y in 0..1: for y in 0..1:
assert t[(x,y)] == $x & $y assert t[(x,y)] == $x & $y
assert($t == 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: block tableTest2:
var t = newTable[string, float]() var t = newTable[string, float]()
@ -340,7 +340,7 @@ block tablesref:
block anonZipTest: block anonZipTest:
let keys = @['a','b','c'] let keys = @['a','b','c']
let values = @[1, 2, 3] 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: block clearTableTest:
var t = newTable[string, float]() var t = newTable[string, float]()

View file

@ -48,7 +48,7 @@ block tableTest1:
for y in 0..1: for y in 0..1:
assert t[(x,y)] == $x & $y assert t[(x,y)] == $x & $y
assert($t == 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: block tableTest2:
var t = initTable[string, float]() var t = initTable[string, float]()

View file

@ -9,23 +9,23 @@ proc test(foo: Foo[int])
required type for foo: Foo[int] required type for foo: Foo[int]
but expression 'bar' is of type: Bar[system.int] but expression 'bar' is of type: Bar[system.int]
t3330.nim(63, 8) Hint: Non-matching candidates for add(k, string, T) 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) proc add[T](x: var seq[T]; y: T)
first type mismatch at position: 1 first type mismatch at position: 1
required type for x: var seq[T] required type for x: var seq[T]
but expression 'k' is of type: Alias 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) proc add(result: var string; x: float)
first type mismatch at position: 1 first type mismatch at position: 1
required type for result: var string required type for result: var string
but expression 'k' is of type: Alias 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 first type mismatch at position: 1
required type for x: var string required type for x: var string
but expression 'k' is of type: Alias 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 first type mismatch at position: 1
required type for x: var string required type for x: var string
but expression 'k' is of type: Alias but expression 'k' is of type: Alias
@ -33,9 +33,9 @@ proc add(result: var string; x: int64)
first type mismatch at position: 1 first type mismatch at position: 1
required type for result: var string required type for result: var string
but expression 'k' is of type: Alias 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 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 but expression 'k' is of type: Alias
t3330.nim(63, 8) template/generic instantiation of `add` from here t3330.nim(63, 8) template/generic instantiation of `add` from here

View 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]

View file

@ -4,14 +4,14 @@ line: 22
nimout: ''' nimout: '''
twrong_at_operator.nim(22, 30) Error: type mismatch: got <array[0..0, type int]> twrong_at_operator.nim(22, 30) Error: type mismatch: got <array[0..0, type int]>
but expected one of: 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] proc `@`[IDX, T](a: sink array[IDX, T]): seq[T]
first type mismatch at position: 1 first type mismatch at position: 1
required type for a: sink array[IDX, T] required type for a: sink array[IDX, T]
but expression '[int]' is of type: array[0..0, type int] 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] expression: @[int]
''' '''

View 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)

View file

@ -762,11 +762,3 @@ block t3717:
var f: Foo[Foo[int]] var f: Foo[Foo[int]]
discard foo(f) 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]

View file

@ -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

View file

@ -0,0 +1,2 @@
proc burnMem*(a: int) =
discard

View file

@ -0,0 +1,2 @@
# module b/utils.nim
let x* = 10

View file

@ -0,0 +1,5 @@
import a/utils as autils, b/utils
# bug #12420
burnMem(x)

View file

@ -1,6 +1,7 @@
discard """ discard """
errormsg: '''ambiguous call; both foobar.async''' errormsg: '''ambiguous call; both foobar.async'''
line: 9 line: 10
disabled: "32bit"
""" """
import foobar import foobar

44
tests/stdlib/tbase64.nim Normal file
View 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()

View file

@ -1,6 +1,8 @@
discard """ discard """
output: "" output: ""
targets: "c js"
""" """
import json, strutils, options, tables import json, strutils, options, tables
when true: when true:
@ -18,6 +20,11 @@ when true:
case kind*: ReplayEventKind case kind*: ReplayEventKind
of FoodAppeared, FoodEaten: of FoodAppeared, FoodEaten:
foodPos*: Point[float] foodPos*: Point[float]
case subKind*: bool
of true:
it: int
of false:
ot: float
of DirectionChanged: of DirectionChanged:
playerPos*: float playerPos*: float
@ -33,7 +40,9 @@ when true:
ReplayEvent( ReplayEvent(
time: 1.2345, time: 1.2345,
kind: FoodEaten, 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, test: 18827361,
@ -592,3 +601,34 @@ static:
doAssert t["fruit"]["color"].getInt == 10 doAssert t["fruit"]["color"].getInt == 10
doAssert t["emails"][0].getStr == "abc" doAssert t["emails"][0].getStr == "abc"
doAssert t["emails"][1].getStr == "123" 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]

View file

@ -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)

View file

@ -334,6 +334,21 @@ block ospaths:
doAssert relativePath("/foo", "/fOO", '/') == (when FileSystemCaseSensitive: "../foo" else: "") doAssert relativePath("/foo", "/fOO", '/') == (when FileSystemCaseSensitive: "../foo" else: "")
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("usr", "") == unixToNativePath"usr/"
doAssert joinPath("", "lib") == "lib" doAssert joinPath("", "lib") == "lib"
doAssert joinPath("", "/lib") == unixToNativePath"/lib" doAssert joinPath("", "/lib") == unixToNativePath"/lib"

View file

@ -4,7 +4,7 @@ abasdfdsmÄhmaИ
Иnastystring Иnastystring
A你好 A你好
ИnastystringA你好 ИnastystringA你好
ÖÜhmabasdfdsmÄhmaИ''' ÖÜhmabasdfdsmÄhmaИOK'''
disabled: "posix" disabled: "posix"
joinable: "false" joinable: "false"
""" """
@ -13,10 +13,10 @@ import winlean
echo "ÄhmÖÜ" echo "ÄhmÖÜ"
echo "abasdfdsmÄhmaИ" echo "abasdfdsmÄhmaИ"
echo "И\0nasty\0\0\0\0string\0" echo "Иnastystring"
echo "A你好" echo "A你好"
write stdout, "И\0nasty\0\0\0\0string\0" write stdout, "Иnastystring"
writeLine stdout, "A你好" writeLine stdout, "A你好"
stdout.flushFile() stdout.flushFile()
@ -25,3 +25,17 @@ var a = "ÖÜhmabasdfdsmÄhmaИ"
var ac = 0'i32 var ac = 0'i32
discard writeFile(handle, addr a[0], int32(len(a)), addr ac, nil) discard writeFile(handle, addr a[0], int32(len(a)), addr ac, nil)
stdout.flushFile() 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"

View file

@ -22,6 +22,9 @@ outputs = [
] ]
for i, expected in enumerate(outputs): for i, expected in enumerate(outputs):
functionSymbol = gdb.selected_frame().block().function
assert functionSymbol.line == 21
if i == 5: if i == 5:
# myArray is passed as pointer to int to myDebug. I look up myArray up in the stack # myArray is passed as pointer to int to myDebug. I look up myArray up in the stack
gdb.execute("up") gdb.execute("up")

View file

@ -1,3 +1,3 @@
Loading Nim Runtime support. 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 8

View file

@ -1,6 +1,8 @@
discard """ discard """
output: '''[127, 127, 0, 255] output: '''[127, 127, 0, 255]
[127, 127, 0, 255] [127, 127, 0, 255]
(data: 1)
''' '''
nimout: '''caught Exception nimout: '''caught Exception
@ -180,3 +182,35 @@ static:
var stream = initCtsStream(file) var stream = initCtsStream(file)
parseAtlas(stream) parseAtlas(stream)
echo "Done!" 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

View file

@ -30,7 +30,7 @@ def getNimRti(type_name):
m = type_hash_regex.match(type_name) m = type_hash_regex.match(type_name)
if m: if m:
try: try:
return gdb.parse_and_eval("NTI_" + m.group(1) + "_") return gdb.parse_and_eval("NTI__" + m.group(1) + "_")
except: except:
return None return None
@ -340,13 +340,13 @@ def reprEnum(e, typ):
return str(e) + " (invalid data!)" return str(e) + " (invalid data!)"
class NimEnumPrinter: 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): def __init__(self, val):
self.val = val self.val = val
match = self.pattern.match(self.val.type.name) match = self.pattern.match(self.val.type.name)
self.typeNimName = match.group(1) self.typeNimName = match.group(1)
typeInfoName = "NTI_" + match.group(2) + "_" typeInfoName = "NTI__" + match.group(2) + "_"
self.nti = gdb.lookup_global_symbol(typeInfoName) self.nti = gdb.lookup_global_symbol(typeInfoName)
if self.nti is None: if self.nti is None:
@ -373,7 +373,7 @@ class NimSetPrinter:
match = self.pattern.match(self.val.type.name) match = self.pattern.match(self.val.type.name)
self.typeNimName = match.group(1) self.typeNimName = match.group(1)
typeInfoName = "NTI_" + match.group(2) + "_" typeInfoName = "NTI__" + match.group(2) + "_"
self.nti = gdb.lookup_global_symbol(typeInfoName) self.nti = gdb.lookup_global_symbol(typeInfoName)
if self.nti is None: if self.nti is None: