diff options
334 files changed, 4638 insertions, 2765 deletions
diff --git a/.gitignore b/.gitignore index 50fa9a431..5476e173f 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,10 @@ dnimcache/ *.o !/icons/*.o +*.obj +*.ilk +*.pdb +*.dll *.exe *.so *.dylib diff --git a/.travis.yml b/.travis.yml index b46d7c830..f28eebc2d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -42,4 +42,5 @@ script: - nimble install niminst - nim c --taintMode:on -d:nimCoroutines tests/testament/tester - tests/testament/tester --pedantic all -d:nimCoroutines + - ./koch web - ./koch csource diff --git a/appveyor.yml b/appveyor.yml index f40fb3e4c..be2cc50d3 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -43,7 +43,6 @@ install: build_script: - bin\nim c koch - - koch boot - koch boot -d:release - koch nimble - nim e tests/test_nimscript.nims diff --git a/compiler/ast.nim b/compiler/ast.nim index ac917346f..0cc4daf22 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -292,6 +292,8 @@ const sfNoForward* = sfRegister # forward declarations are not required (per module) + sfReorder* = sfForward + # reordering pass is enabled sfCompileToCpp* = sfInfixCall # compile the module as C++ code sfCompileToObjc* = sfNamedParamCall # compile the module as Objective-C code diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 0ec16710f..f5c793d29 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -744,14 +744,17 @@ proc genTupleElem(p: BProc, e: PNode, d: var TLoc) = addf(r, ".Field$1", [rope(i)]) putIntoDest(p, d, tupType.sons[i], r, a.s) -proc lookupFieldAgain(p: BProc, ty: PType; field: PSym; r: var Rope): PSym = +proc lookupFieldAgain(p: BProc, ty: PType; field: PSym; r: var Rope; + resTyp: ptr PType = nil): PSym = var ty = ty assert r != nil while ty != nil: ty = ty.skipTypes(skipPtrs) assert(ty.kind in {tyTuple, tyObject}) result = lookupInRecord(ty.n, field.name) - if result != nil: break + if result != nil: + if resTyp != nil: resTyp[] = ty + break if not p.module.compileToCpp: add(r, ".Sup") ty = ty.sons[0] if result == nil: internalError(field.info, "genCheckedRecordField") @@ -768,8 +771,9 @@ proc genRecordField(p: BProc, e: PNode, d: var TLoc) = addf(r, ".Field$1", [rope(f.position)]) putIntoDest(p, d, f.typ, r, a.s) else: - let field = lookupFieldAgain(p, ty, f, r) - if field.loc.r == nil: fillObjectFields(p.module, ty) + var rtyp: PType + let field = lookupFieldAgain(p, ty, f, r, addr rtyp) + if field.loc.r == nil and rtyp != nil: fillObjectFields(p.module, rtyp) if field.loc.r == nil: internalError(e.info, "genRecordField 3 " & typeToString(ty)) addf(r, ".$1", [field.loc.r]) putIntoDest(p, d, field.typ, r, a.s) @@ -1057,7 +1061,7 @@ proc genSeqElemAppend(p: BProc, e: PNode, d: var TLoc) = "$1 = ($2) #incrSeqV2(&($1)->Sup, sizeof($3));$n" else: "$1 = ($2) #incrSeqV2($1, sizeof($3));$n" - var a, b, dest: TLoc + var a, b, dest, tmpL: TLoc initLocExpr(p, e.sons[1], a) initLocExpr(p, e.sons[2], b) let bt = skipTypes(e.sons[2].typ, {tyVar}) @@ -1068,9 +1072,10 @@ proc genSeqElemAppend(p: BProc, e: PNode, d: var TLoc) = #if bt != b.t: # echo "YES ", e.info, " new: ", typeToString(bt), " old: ", typeToString(b.t) initLoc(dest, locExpr, bt, OnHeap) - dest.r = rfmt(nil, "$1->data[$1->$2]", rdLoc(a), lenField(p)) + getIntTemp(p, tmpL) + lineCg(p, cpsStmts, "$1 = $2->$3++;$n", tmpL.r, rdLoc(a), lenField(p)) + dest.r = rfmt(nil, "$1->data[$2]", rdLoc(a), tmpL.r) genAssignment(p, dest, b, {needToCopy, afDestIsNil}) - lineCg(p, cpsStmts, "++$1->$2;$n", rdLoc(a), lenField(p)) gcUsage(e) proc genReset(p: BProc, n: PNode) = @@ -1096,9 +1101,9 @@ proc rawGenNew(p: BProc, a: TLoc, sizeExpr: Rope) = if a.s == OnHeap and usesNativeGC(): # use newObjRC1 as an optimization if canFormAcycle(a.t): - linefmt(p, cpsStmts, "if ($1) #nimGCunref($1);$n", a.rdLoc) + linefmt(p, cpsStmts, "if ($1) { #nimGCunrefRC1($1); $1 = NIM_NIL; }$n", a.rdLoc) else: - linefmt(p, cpsStmts, "if ($1) #nimGCunrefNoCycle($1);$n", a.rdLoc) + linefmt(p, cpsStmts, "if ($1) { #nimGCunrefNoCycle($1); $1 = NIM_NIL; }$n", a.rdLoc) b.r = ropecg(p.module, "($1) #newObjRC1($2, $3)", args) linefmt(p, cpsStmts, "$1 = $2;$n", a.rdLoc, b.rdLoc) else: @@ -1126,9 +1131,9 @@ proc genNewSeqAux(p: BProc, dest: TLoc, length: Rope) = initLoc(call, locExpr, dest.t, OnHeap) if dest.s == OnHeap and usesNativeGC(): if canFormAcycle(dest.t): - linefmt(p, cpsStmts, "if ($1) #nimGCunref($1);$n", dest.rdLoc) + linefmt(p, cpsStmts, "if ($1) { #nimGCunrefRC1($1); $1 = NIM_NIL; }$n", dest.rdLoc) else: - linefmt(p, cpsStmts, "if ($1) #nimGCunrefNoCycle($1);$n", dest.rdLoc) + linefmt(p, cpsStmts, "if ($1) { #nimGCunrefNoCycle($1); $1 = NIM_NIL; }$n", dest.rdLoc) call.r = ropecg(p.module, "($1) #newSeqRC1($2, $3)", args) linefmt(p, cpsStmts, "$1 = $2;$n", dest.rdLoc, call.rdLoc) else: @@ -1170,7 +1175,10 @@ proc handleConstExpr(p: BProc, n: PNode, d: var TLoc): bool = proc genObjConstr(p: BProc, e: PNode, d: var TLoc) = #echo rendertree e, " ", e.isDeepConstExpr - if handleConstExpr(p, e, d): return + # inheritance in C++ does not allow struct initialization so + # we skip this step here: + if not p.module.compileToCpp: + if handleConstExpr(p, e, d): return var tmp: TLoc var t = e.typ.skipTypes(abstractInst) getTemp(p, t, tmp) @@ -1378,13 +1386,30 @@ proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) = useStringh(p.module) if op == mHigh: unaryExpr(p, e, d, "($1 ? (strlen($1)-1) : -1)") else: unaryExpr(p, e, d, "($1 ? strlen($1) : 0)") - of tyString, tySequence: + of tyString: if not p.module.compileToCpp: if op == mHigh: unaryExpr(p, e, d, "($1 ? ($1->Sup.len-1) : -1)") else: unaryExpr(p, e, d, "($1 ? $1->Sup.len : 0)") else: if op == mHigh: unaryExpr(p, e, d, "($1 ? ($1->len-1) : -1)") else: unaryExpr(p, e, d, "($1 ? $1->len : 0)") + of tySequence: + var a, tmp: TLoc + initLocExpr(p, e[1], a) + getIntTemp(p, tmp) + var frmt: FormatStr + if not p.module.compileToCpp: + if op == mHigh: + frmt = "$1 = ($2 ? ($2->Sup.len-1) : -1);$n" + else: + frmt = "$1 = ($2 ? $2->Sup.len : 0);$n" + else: + if op == mHigh: + frmt = "$1 = ($2 ? ($2->len-1) : -1);$n" + else: + frmt = "$1 = ($2 ? $2->len : 0);$n" + lineCg(p, cpsStmts, frmt, tmp.r, rdLoc(a)) + putIntoDest(p, d, e.typ, tmp.r) of tyArray: # YYY: length(sideeffect) is optimized away incorrectly? if op == mHigh: putIntoDest(p, d, e.typ, rope(lastOrd(typ))) @@ -1742,11 +1767,23 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of mOrd: genOrd(p, e, d) of mLengthArray, mHigh, mLengthStr, mLengthSeq, mLengthOpenArray: genArrayLen(p, e, d, op) - of mXLenStr, mXLenSeq: + of mXLenStr: if not p.module.compileToCpp: unaryExpr(p, e, d, "($1->Sup.len)") else: unaryExpr(p, e, d, "$1->len") + of mXLenSeq: + # see 'taddhigh.nim' for why we need to use a temporary here: + var a, tmp: TLoc + initLocExpr(p, e[1], a) + getIntTemp(p, tmp) + var frmt: FormatStr + if not p.module.compileToCpp: + frmt = "$1 = $2->Sup.len;$n" + else: + frmt = "$1 = $2->len;$n" + lineCg(p, cpsStmts, frmt, tmp.r, rdLoc(a)) + putIntoDest(p, d, e.typ, tmp.r) of mGCref: unaryStmt(p, e, d, "#nimGCref($1);$n") of mGCunref: unaryStmt(p, e, d, "#nimGCunref($1);$n") of mSetLengthStr: genSetLengthStr(p, e, d) @@ -2123,17 +2160,19 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = # See tests/run/tcnstseq3 for an example that would fail otherwise. genAsgn(p, n, fastAsgn=p.prc != nil) of nkDiscardStmt: - if n.sons[0].kind != nkEmpty: + let ex = n[0] + if ex.kind != nkEmpty: genLineDir(p, n) var a: TLoc - if n[0].kind in nkCallKinds: + if ex.kind in nkCallKinds and (ex[0].kind != nkSym or + ex[0].sym.magic == mNone): # bug #6037: do not assign to a temp in C++ mode: incl a.flags, lfSingleUse - genCall(p, n[0], a) + genCall(p, ex, a) if lfSingleUse notin a.flags: line(p, cpsStmts, a.r & ";" & tnl) else: - initLocExpr(p, n.sons[0], a) + initLocExpr(p, ex, a) of nkAsmStmt: genAsmStmt(p, n) of nkTryStmt: if p.module.compileToCpp and optNoCppExceptions notin gGlobalOptions: @@ -2201,20 +2240,22 @@ proc getDefaultValue(p: BProc; typ: PType; info: TLineInfo): Rope = else: globalError(info, "cannot create null element for: " & $t.kind) -proc getNullValueAux(p: BProc; obj, cons: PNode, result: var Rope) = +proc getNullValueAux(p: BProc; t: PType; obj, cons: PNode, result: var Rope; count: var int) = case obj.kind of nkRecList: - for i in countup(0, sonsLen(obj) - 1): getNullValueAux(p, obj.sons[i], cons, result) + for i in countup(0, sonsLen(obj) - 1): + getNullValueAux(p, t, obj.sons[i], cons, result, count) of nkRecCase: - getNullValueAux(p, obj.sons[0], cons, result) + getNullValueAux(p, t, obj.sons[0], cons, result, count) for i in countup(1, sonsLen(obj) - 1): - getNullValueAux(p, lastSon(obj.sons[i]), cons, result) + getNullValueAux(p, t, lastSon(obj.sons[i]), cons, result, count) of nkSym: - if not result.isNil: result.add ", " + if count > 0: result.add ", " + inc count let field = obj.sym for i in 1..<cons.len: if cons[i].kind == nkExprColonExpr: - if cons[i][0].sym.name == field.name: + if cons[i][0].sym.name.id == field.name.id: result.add genConstExpr(p, cons[i][1]) return elif i == field.position: @@ -2225,14 +2266,32 @@ proc getNullValueAux(p: BProc; obj, cons: PNode, result: var Rope) = else: localError(cons.info, "cannot create null element for: " & $obj) +proc getNullValueAuxT(p: BProc; orig, t: PType; obj, cons: PNode, result: var Rope; count: var int) = + var base = t.sons[0] + let oldRes = result + if not p.module.compileToCpp: result.add "{" + let oldcount = count + if base != nil: + base = skipTypes(base, skipPtrs) + getNullValueAuxT(p, orig, base, base.n, cons, result, count) + elif not isObjLackingTypeField(t) and not p.module.compileToCpp: + addf(result, "$1", [genTypeInfo(p.module, orig)]) + inc count + getNullValueAux(p, t, obj, cons, result, count) + # do not emit '{}' as that is not valid C: + if oldcount == count: result = oldres + elif not p.module.compileToCpp: result.add "}" + proc genConstObjConstr(p: BProc; n: PNode): Rope = - var length = sonsLen(n) result = nil let t = n.typ.skipTypes(abstractInst) - if not isObjLackingTypeField(t) and not p.module.compileToCpp: - addf(result, "{$1}", [genTypeInfo(p.module, t)]) - getNullValueAux(p, t.n, n, result) - result = "{$1}$n" % [result] + var count = 0 + #if not isObjLackingTypeField(t) and not p.module.compileToCpp: + # addf(result, "{$1}", [genTypeInfo(p.module, t)]) + # inc count + getNullValueAuxT(p, t, t, t.n, n, result, count) + if p.module.compileToCpp: + result = "{$1}$n" % [result] proc genConstSimpleList(p: BProc, n: PNode): Rope = var length = sonsLen(n) @@ -2279,6 +2338,15 @@ proc genConstExpr(p: BProc, n: PNode): Rope = var t = skipTypes(n.typ, abstractInst) if t.kind == tySequence: result = genConstSeq(p, n, n.typ) + elif t.kind == tyProc and t.callConv == ccClosure and not n.sons.isNil and + n.sons[0].kind == nkNilLit and n.sons[1].kind == nkNilLit: + # this hack fixes issue that nkNilLit is expanded to {NIM_NIL,NIM_NIL} + # this behaviour is needed since closure_var = nil must be + # expanded to {NIM_NIL,NIM_NIL} + # in VM closures are initialized with nkPar(nkNilLit, nkNilLit) + # leading to duplicate code like this: + # "{NIM_NIL,NIM_NIL}, {NIM_NIL,NIM_NIL}" + result = ~"{NIM_NIL,NIM_NIL}" else: result = genConstSimpleList(p, n) of nkObjConstr: diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index 378951d9d..8796dd729 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -164,11 +164,11 @@ proc genBreakState(p: BProc, n: PNode) = if n.sons[0].kind == nkClosure: # XXX this produces quite inefficient code! initLocExpr(p, n.sons[0].sons[1], a) - lineF(p, cpsStmts, "if (((NI*) $1)[0] < 0) break;$n", [rdLoc(a)]) + lineF(p, cpsStmts, "if (((NI*) $1)[1] < 0) break;$n", [rdLoc(a)]) else: initLocExpr(p, n.sons[0], a) - # the environment is guaranteed to contain the 'state' field at offset 0: - lineF(p, cpsStmts, "if ((((NI*) $1.ClE_0)[0]) < 0) break;$n", [rdLoc(a)]) + # the environment is guaranteed to contain the 'state' field at offset 1: + lineF(p, cpsStmts, "if ((((NI*) $1.ClE_0)[1]) < 0) break;$n", [rdLoc(a)]) # lineF(p, cpsStmts, "if (($1) < 0) break;$n", [rdLoc(a)]) proc genVarPrototypeAux(m: BModule, sym: PSym) diff --git a/compiler/ccgtrav.nim b/compiler/ccgtrav.nim index 982f88cbd..fa228ff04 100644 --- a/compiler/ccgtrav.nim +++ b/compiler/ccgtrav.nim @@ -72,10 +72,16 @@ proc genTraverseProc(c: var TTraversalClosure, accessor: Rope, typ: PType) = let arraySize = lengthOrd(typ.sons[0]) var i: TLoc getTemp(p, getSysType(tyInt), i) + let oldCode = p.s(cpsStmts) linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n", i.r, arraySize.rope) + let oldLen = p.s(cpsStmts).len genTraverseProc(c, rfmt(nil, "$1[$2]", accessor, i.r), typ.sons[1]) - lineF(p, cpsStmts, "}$n", []) + if p.s(cpsStmts).len == oldLen: + # do not emit dummy long loops for faster debug builds: + p.s(cpsStmts) = oldCode + else: + lineF(p, cpsStmts, "}$n", []) of tyObject: for i in countup(0, sonsLen(typ) - 1): var x = typ.sons[i] @@ -99,10 +105,16 @@ proc genTraverseProcSeq(c: var TTraversalClosure, accessor: Rope, typ: PType) = assert typ.kind == tySequence var i: TLoc getTemp(p, getSysType(tyInt), i) + let oldCode = p.s(cpsStmts) lineF(p, cpsStmts, "for ($1 = 0; $1 < $2->$3; $1++) {$n", [i.r, accessor, rope(if c.p.module.compileToCpp: "len" else: "Sup.len")]) + let oldLen = p.s(cpsStmts).len genTraverseProc(c, "$1->data[$2]" % [accessor, i.r], typ.sons[0]) - lineF(p, cpsStmts, "}$n", []) + if p.s(cpsStmts).len == oldLen: + # do not emit dummy long loops for faster debug builds: + p.s(cpsStmts) = oldCode + else: + lineF(p, cpsStmts, "}$n", []) proc genTraverseProc(m: BModule, origTyp: PType; sig: SigHash; reason: TTypeInfoReason): Rope = diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 0c81ca814..35d73aac0 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -12,6 +12,7 @@ # ------------------------- Name Mangling -------------------------------- import sighashes +from lowerings import createObj proc isKeyword(w: PIdent): bool = # Nim and C++ share some keywords @@ -122,10 +123,11 @@ const proc typeName(typ: PType): Rope = let typ = typ.skipTypes(irrelevantForBackend) - result = if typ.sym != nil and typ.kind in {tyObject, tyEnum}: - typ.sym.name.s.mangle.rope - else: - ~"TY" + result = + if typ.sym != nil and typ.kind in {tyObject, tyEnum}: + rope($typ.kind & '_' & typ.sym.name.s.mangle) + else: + rope($typ.kind) proc getTypeName(m: BModule; typ: PType; sig: SigHash): Rope = var t = typ @@ -335,6 +337,7 @@ proc getTypePre(m: BModule, typ: PType; sig: SigHash): Rope = if result == nil: result = cacheGetType(m.typeCache, sig) proc structOrUnion(t: PType): Rope = + let t = t.skipTypes({tyAlias}) (if tfUnion in t.flags: rope("union") else: rope("struct")) proc getForwardStructFormat(m: BModule): string = @@ -473,11 +476,14 @@ proc genRecordFieldsAux(m: BModule, n: PNode, if tfPacked notin rectype.flags: add(unionBody, "struct {") else: - addf(unionBody, CC[cCompiler].structStmtFmt, - [rope"struct", nil, rope(CC[cCompiler].packedPragma)]) - add(unionBody, "{") + if hasAttribute in CC[cCompiler].props: + add(unionBody, "struct __attribute__((__packed__)){" ) + else: + addf(unionBody, "#pragma pack(1)$nstruct{", []) add(unionBody, a) addf(unionBody, "} $1;$n", [sname]) + if tfPacked in rectype.flags and hasAttribute notin CC[cCompiler].props: + addf(unionBody, "#pragma pack(pop)$n", []) else: add(unionBody, genRecordFieldsAux(m, k, ae, rectype, check)) else: internalError("genRecordFieldsAux(record case branch)") @@ -524,12 +530,16 @@ proc getRecordDesc(m: BModule, typ: PType, name: Rope, # declare the record: var hasField = false - var attribute: Rope = - if tfPacked in typ.flags: rope(CC[cCompiler].packedPragma) - else: nil + if tfPacked in typ.flags: + if hasAttribute in CC[cCompiler].props: + result = structOrUnion(typ) & " __attribute__((__packed__))" + else: + result = "#pragma pack(1)" & tnl & structOrUnion(typ) + else: + result = structOrUnion(typ) - result = ropecg(m, CC[cCompiler].structStmtFmt, - [structOrUnion(typ), name, attribute]) + result.add " " + result.add name if typ.kind == tyObject: @@ -537,7 +547,7 @@ proc getRecordDesc(m: BModule, typ: PType, name: Rope, if (typ.sym != nil and sfPure in typ.sym.flags) or tfFinal in typ.flags: appcg(m, result, " {$n", []) else: - appcg(m, result, " {$n#TNimType* m_type;$n", [name, attribute]) + appcg(m, result, " {$n#TNimType* m_type;$n", []) hasField = true elif m.compileToCpp: appcg(m, result, " : public $1 {$n", @@ -556,6 +566,8 @@ proc getRecordDesc(m: BModule, typ: PType, name: Rope, else: add(result, desc) add(result, "};" & tnl) + if tfPacked in typ.flags and hasAttribute notin CC[cCompiler].props: + result.add "#pragma pack(pop)" & tnl proc getTupleDesc(m: BModule, typ: PType, name: Rope, check: var IntSet): Rope = @@ -787,7 +799,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope = add(m.s[cfsTypes], recdesc) elif tfIncompleteStruct notin t.flags: addAbiCheck(m, t, result) of tySet: - result = getTypeName(m, t.lastSon, hashType t.lastSon) & "_Set" + result = $t.kind & '_' & getTypeName(m, t.lastSon, hashType t.lastSon) m.typeCache[sig] = result if not isImportedType(t): let s = int(getSize(t)) @@ -1083,7 +1095,8 @@ proc fakeClosureType(owner: PSym): PType = result = newType(tyTuple, owner) result.rawAddSon(newType(tyPointer, owner)) var r = newType(tyRef, owner) - r.rawAddSon(newType(tyTuple, owner)) + let obj = createObj(owner, owner.info, final=false) + r.rawAddSon(obj) result.rawAddSon(r) type diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 3797a92c2..b618837c7 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -209,7 +209,7 @@ proc genLineDir(p: BProc, t: PNode) = if ({optStackTrace, optEndb} * p.options == {optStackTrace, optEndb}) and (p.prc == nil or sfPure notin p.prc.flags): if freshLineInfo(p, tt.info): - linefmt(p, cpsStmts, "#endb($1, $2);$n", + linefmt(p, cpsStmts, "#endb($1, $2);$N", line.rope, makeCString(toFilename(tt.info))) elif ({optLineTrace, optStackTrace} * p.options == {optLineTrace, optStackTrace}) and @@ -345,6 +345,15 @@ proc getTemp(p: BProc, t: PType, result: var TLoc; needsInit=false) = result.flags = {} constructLoc(p, result, not needsInit) +proc getIntTemp(p: BProc, result: var TLoc) = + inc(p.labels) + result.r = "T" & rope(p.labels) & "_" + linefmt(p, cpsLocals, "NI $1;$n", result.r) + result.k = locTemp + result.s = OnStack + result.t = getSysType(tyInt) + result.flags = {} + proc initGCFrame(p: BProc): Rope = if p.gcFrameId > 0: result = "struct {$1} GCFRAME_;$n" % [p.gcFrameType] @@ -385,7 +394,8 @@ proc assignLocalVar(p: BProc, s: PSym) = #assert(s.loc.k == locNone) # not yet assigned # this need not be fulfilled for inline procs; they are regenerated # for each module that uses them! - let decl = localVarDecl(p, s) & ";" & tnl + let nl = if optLineDir in gOptions: "" else: tnl + let decl = localVarDecl(p, s) & ";" & nl line(p, cpsLocals, decl) localDebugInfo(p, s) @@ -618,11 +628,11 @@ proc initFrame(p: BProc, procname, filename: Rope): Rope = discard cgsym(p.module, "nimFrame") if p.maxFrameLen > 0: discard cgsym(p.module, "VarSlot") - result = rfmt(nil, "\tnimfrs_($1, $2, $3, $4)$N", + result = rfmt(nil, "\tnimfrs_($1, $2, $3, $4);$n", procname, filename, p.maxFrameLen.rope, p.blocks[0].frameLen.rope) else: - result = rfmt(nil, "\tnimfr_($1, $2)$N", procname, filename) + result = rfmt(nil, "\tnimfr_($1, $2);$n", procname, filename) proc deinitFrame(p: BProc): Rope = result = rfmt(p.module, "\t#popFrame();$n") @@ -1302,6 +1312,7 @@ proc myProcess(b: PPassContext, n: PNode): PNode = if b == nil or passes.skipCodegen(n): return var m = BModule(b) m.initProc.options = initProcOptions(m) + softRnl = if optLineDir in gOptions: noRnl else: rnl genStmts(m.initProc, n) proc finishModule(m: BModule) = diff --git a/compiler/commands.nim b/compiler/commands.nim index 22e4b5a2c..9781a8af4 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -207,7 +207,7 @@ proc testCompileOptionArg*(switch, arg: string, info: TLineInfo): bool = of "generational": result = gSelectedGC == gcGenerational of "go": result = gSelectedGC == gcGo of "none": result = gSelectedGC == gcNone - of "stack": result = gSelectedGC == gcStack + of "stack", "regions": result = gSelectedGC == gcRegions else: localError(info, errNoneBoehmRefcExpectedButXFound, arg) of "opt": case arg.normalize @@ -429,9 +429,9 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; of "none": gSelectedGC = gcNone defineSymbol("nogc") - of "stack": - gSelectedGC= gcStack - defineSymbol("gcstack") + of "stack", "regions": + gSelectedGC= gcRegions + defineSymbol("gcregions") else: localError(info, errNoneBoehmRefcExpectedButXFound, arg) of "warnings", "w": if processOnOffSwitchOrList({optWarns}, arg, pass, info): listWarnings() @@ -454,6 +454,7 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; of "native", "gdb": incl(gGlobalOptions, optCDebug) gOptions = gOptions + {optLineDir} - {optEndb} + defineSymbol("nimTypeNames", nil) # type names are used in gdb pretty printing undefSymbol("endb") else: localError(info, "expected endb|gdb but found " & arg) diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index a4f47ac72..dc97e3648 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -41,7 +41,10 @@ proc isDefined*(symbol: string): bool = result = targetOS in {osLinux, osMorphos, osSkyos, osIrix, osPalmos, osQnx, osAtari, osAix, osHaiku, osVxWorks, osSolaris, osNetbsd, - osFreebsd, osOpenbsd, osDragonfly, osMacosx} + osFreebsd, osOpenbsd, osDragonfly, osMacosx, + osAndroid} + of "linux": + result = targetOS in {osLinux, osAndroid} of "bsd": result = targetOS in {osNetbsd, osFreebsd, osOpenbsd, osDragonfly} of "emulatedthreadvars": diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 26dd889ce..3f4f7b164 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -462,12 +462,14 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) = var path = n.info.toFullPath if path.startsWith(cwd): path = path[cwd.len+1 .. ^1].replace('\\', '/') - var commit = getConfigVar("git.commit") - if commit.len == 0: commit = "master" - dispA(seeSrcRope, "$1", "", [ropeFormatNamedVars(docItemSeeSrc, - ["path", "line", "url", "commit"], [rope path, - rope($n.info.line), rope getConfigVar("git.url"), - rope commit])]) + let gitUrl = getConfigVar("git.url") + if gitUrl.len > 0: + var commit = getConfigVar("git.commit") + if commit.len == 0: commit = "master" + dispA(seeSrcRope, "$1", "", [ropeFormatNamedVars(docItemSeeSrc, + ["path", "line", "url", "commit"], [rope path, + rope($n.info.line), rope gitUrl, + rope commit])]) add(d.section[k], ropeFormatNamedVars(getConfigVar("doc.item"), ["name", "header", "desc", "itemID", "header_plain", "itemSym", diff --git a/compiler/evalffi.nim b/compiler/evalffi.nim index 987cfaf42..6789df87d 100644 --- a/compiler/evalffi.nim +++ b/compiler/evalffi.nim @@ -104,9 +104,9 @@ proc mapCallConv(cc: TCallingConvention, info: TLineInfo): TABI = else: globalError(info, "cannot map calling convention to FFI") -template rd(T, p: expr): expr {.immediate.} = (cast[ptr T](p))[] -template wr(T, p, v: expr) {.immediate.} = (cast[ptr T](p))[] = v -template `+!`(x, y: expr): expr {.immediate.} = +template rd(T, p: untyped): untyped = (cast[ptr T](p))[] +template wr(T, p, v: untyped): untyped = (cast[ptr T](p))[] = v +template `+!`(x, y: untyped): untyped = cast[pointer](cast[ByteAddress](x) + y) proc packSize(v: PNode, typ: PType): int = @@ -171,7 +171,7 @@ const maxPackDepth = 20 var packRecCheck = 0 proc pack(v: PNode, typ: PType, res: pointer) = - template awr(T, v: expr) {.immediate, dirty.} = + template awr(T, v: untyped): untyped = wr(T, res, v) case typ.kind @@ -302,7 +302,7 @@ proc canonNodeKind(k: TNodeKind): TNodeKind = else: result = k proc unpack(x: pointer, typ: PType, n: PNode): PNode = - template aw(k, v, field: expr) {.immediate, dirty.} = + template aw(k, v, field: untyped): untyped = if n.isNil: result = newNode(k) result.typ = typ @@ -326,9 +326,9 @@ proc unpack(x: pointer, typ: PType, n: PNode): PNode = result.kind = nkNilLit result.typ = typ - template awi(kind, v: expr) {.immediate, dirty.} = aw(kind, v, intVal) - template awf(kind, v: expr) {.immediate, dirty.} = aw(kind, v, floatVal) - template aws(kind, v: expr) {.immediate, dirty.} = aw(kind, v, strVal) + template awi(kind, v: untyped): untyped = aw(kind, v, intVal) + template awf(kind, v: untyped): untyped = aw(kind, v, floatVal) + template aws(kind, v: untyped): untyped = aw(kind, v, strVal) case typ.kind of tyBool: awi(nkIntLit, rd(bool, x).ord) diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index ca4f621e4..c47e4fb9a 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -53,7 +53,6 @@ type # used on some platforms asmStmtFrmt: string, # format of ASM statement structStmtFmt: string, # Format for struct statement - packedPragma: string, # Attribute/pragma to make struct packed (1-byte aligned) props: TInfoCCProps] # properties of the C compiler @@ -86,7 +85,6 @@ compiler gcc: pic: "-fPIC", asmStmtFrmt: "asm($1);$n", structStmtFmt: "$1 $3 $2 ", # struct|union [packed] $name - packedPragma: "__attribute__((__packed__))", props: {hasSwitchRange, hasComputedGoto, hasCpp, hasGcGuard, hasGnuAsm, hasAttribute}) @@ -129,7 +127,6 @@ compiler vcc: pic: "", asmStmtFrmt: "__asm{$n$1$n}$n", structStmtFmt: "$3$n$1 $2", - packedPragma: "#pragma pack(1)", props: {hasCpp, hasAssume, hasDeclspec}) # Intel C/C++ Compiler @@ -166,7 +163,6 @@ compiler lcc: pic: "", asmStmtFrmt: "_asm{$n$1$n}$n", structStmtFmt: "$1 $2", - packedPragma: "", # XXX: not supported yet props: {}) # Borland C Compiler @@ -191,7 +187,6 @@ compiler bcc: pic: "", asmStmtFrmt: "__asm{$n$1$n}$n", structStmtFmt: "$1 $2", - packedPragma: "", # XXX: not supported yet props: {hasCpp}) # Digital Mars C Compiler @@ -216,7 +211,6 @@ compiler dmc: pic: "", asmStmtFrmt: "__asm{$n$1$n}$n", structStmtFmt: "$3$n$1 $2", - packedPragma: "#pragma pack(1)", props: {hasCpp}) # Watcom C Compiler @@ -241,7 +235,6 @@ compiler wcc: pic: "", asmStmtFrmt: "__asm{$n$1$n}$n", structStmtFmt: "$1 $2", - packedPragma: "", # XXX: not supported yet props: {hasCpp}) # Tiny C Compiler @@ -266,7 +259,6 @@ compiler tcc: pic: "", asmStmtFrmt: "__asm{$n$1$n}$n", structStmtFmt: "$1 $2", - packedPragma: "", # XXX: not supported yet props: {hasSwitchRange, hasComputedGoto}) # Pelles C Compiler @@ -292,7 +284,6 @@ compiler pcc: pic: "", asmStmtFrmt: "__asm{$n$1$n}$n", structStmtFmt: "$1 $2", - packedPragma: "", # XXX: not supported yet props: {}) # Your C Compiler @@ -317,7 +308,6 @@ compiler ucc: pic: "", asmStmtFrmt: "__asm{$n$1$n}$n", structStmtFmt: "$1 $2", - packedPragma: "", # XXX: not supported yet props: {}) const @@ -687,11 +677,14 @@ proc getLinkCmd(projectfile, objfiles: string): string = exefile = quoteShell(exefile) let linkOptions = getLinkOptions() & " " & getConfigVar(cCompiler, ".options.linker") + var linkTmpl = getConfigVar(cCompiler, ".linkTmpl") + if linkTmpl.len == 0: + linkTmpl = CC[cCompiler].linkTmpl result = quoteShell(result % ["builddll", builddll, "buildgui", buildgui, "options", linkOptions, "objfiles", objfiles, "exefile", exefile, "nim", getPrefixDir(), "lib", libpath]) result.add ' ' - addf(result, CC[cCompiler].linkTmpl, ["builddll", builddll, + addf(result, linkTmpl, ["builddll", builddll, "buildgui", buildgui, "options", linkOptions, "objfiles", objfiles, "exefile", exefile, "nim", quoteShell(getPrefixDir()), diff --git a/compiler/installer.ini b/compiler/installer.ini index 8cc89da9f..8052121bf 100644 --- a/compiler/installer.ini +++ b/compiler/installer.ini @@ -6,7 +6,7 @@ Name: "Nim" Version: "$version" Platforms: """ windows: i386;amd64 - linux: i386;amd64;powerpc64;arm;sparc;mips;mipsel;powerpc;powerpc64el;arm64 + linux: i386;amd64;powerpc64;arm;sparc;mips;mipsel;mips64;mips64el;powerpc;powerpc64el;arm64 macosx: i386;amd64;powerpc64 solaris: i386;amd64;sparc;sparc64 freebsd: i386;amd64 @@ -14,6 +14,7 @@ Platforms: """ openbsd: i386;amd64 dragonfly: i386;amd64 haiku: i386;amd64 + android: i386;arm;arm64 """ Authors: "Andreas Rumpf" @@ -47,7 +48,7 @@ Start: "doc/html/overview.html" [Other] -Files: "readme.txt;copying.txt" +Files: "readme.txt;copying.txt;install.txt" Files: "makefile" Files: "koch.nim" Files: "install_nimble.nims" diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 46766cfcf..73e6a9948 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -2340,7 +2340,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) = of nkPragma: genPragma(p, n) of nkProcDef, nkMethodDef, nkConverterDef: var s = n.sons[namePos].sym - if sfExportc in s.flags and compilingLib: + if {sfExportc, sfCompilerProc} * s.flags == {sfExportc}: genSym(p, n.sons[namePos], r) r.res = nil of nkGotoState, nkState: diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index cd2ccfe53..986d8c716 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -142,7 +142,7 @@ proc createStateField(iter: PSym): PSym = proc createEnvObj(owner: PSym; info: TLineInfo): PType = # YYY meh, just add the state field for every closure for now, it's too # hard to figure out if it comes from a closure iterator: - result = createObj(owner, info) + result = createObj(owner, info, final=false) rawAddField(result, createStateField(owner)) proc getIterResult(iter: PSym): PSym = diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim index 4bd54603d..ce76b63a4 100644 --- a/compiler/lowerings.nim +++ b/compiler/lowerings.nim @@ -109,17 +109,19 @@ proc lowerSwap*(n: PNode; owner: PSym): PNode = result.add newFastAsgnStmt(n[1], n[2]) result.add newFastAsgnStmt(n[2], tempAsNode) -proc createObj*(owner: PSym, info: TLineInfo): PType = +proc createObj*(owner: PSym, info: TLineInfo; final=true): PType = result = newType(tyObject, owner) - rawAddSon(result, nil) - incl result.flags, tfFinal + if final: + rawAddSon(result, nil) + incl result.flags, tfFinal + else: + rawAddSon(result, getCompilerProc("RootObj").typ) result.n = newNodeI(nkRecList, info) - when true: - let s = newSym(skType, getIdent("Env_" & info.toFilename), - owner, info) - incl s.flags, sfAnon - s.typ = result - result.sym = s + let s = newSym(skType, getIdent("Env_" & info.toFilename), + owner, info) + incl s.flags, sfAnon + s.typ = result + result.sym = s proc rawAddField*(obj: PType; field: PSym) = assert field.kind == skField diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 5f4a0caf1..4a1166f51 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -746,7 +746,7 @@ proc toFileLine*(info: TLineInfo): string {.inline.} = result = info.toFilename & ":" & $info.line proc toFileLineCol*(info: TLineInfo): string {.inline.} = - result = info.toFilename & "(" & $info.line & "," & $info.col & ")" + result = info.toFilename & "(" & $info.line & ", " & $info.col & ")" proc `$`*(info: TLineInfo): string = toFileLineCol(info) diff --git a/compiler/nimblecmd.nim b/compiler/nimblecmd.nim index 5e6d843de..8042644b0 100644 --- a/compiler/nimblecmd.nim +++ b/compiler/nimblecmd.nim @@ -9,7 +9,7 @@ ## Implements some helper procs for Nimble (Nim's package manager) support. -import parseutils, strutils, strtabs, os, options, msgs +import parseutils, strutils, strtabs, os, options, msgs, sequtils proc addPath*(path: string, info: TLineInfo) = if not options.searchPaths.contains(path): @@ -21,51 +21,76 @@ proc versionSplitPos(s: string): int = while result > 1 and s[result] != '-': dec result if s[result] != '-': result = s.len -const - latest = "" - -proc `<.`(a, b: string): bool = - # wether a has a smaller version than b: - if a == latest: return true - elif b == latest: return false - var i = 0 - var j = 0 - var verA = 0 - var verB = 0 - while true: - let ii = parseInt(a, verA, i) - let jj = parseInt(b, verB, j) - if ii <= 0 or jj <= 0: - # if A has no number and B has but A has no number whatsoever ("#head"), - # A is preferred: - if ii > 0 and jj <= 0 and j == 0: return true - if ii <= 0 and jj > 0 and i == 0: return false - # if A has no number left, but B has, B is preferred: 0.8 vs 0.8.3 - return jj > 0 - if verA < verB: return true - elif verA > verB: return false - # else: same version number; continue: - inc i, ii - inc j, jj - if a[i] == '.': inc i - if b[j] == '.': inc j +type + Version = distinct string + +proc `$`(ver: Version): string {.borrow.} + +proc newVersion(ver: string): Version = + doAssert(ver.len == 0 or ver[0] in {'#', '\0'} + Digits, + "Wrong version: " & ver) + return Version(ver) + +proc isSpecial(ver: Version): bool = + return ($ver).len > 0 and ($ver)[0] == '#' + +proc `<`(ver: Version, ver2: Version): bool = + ## This is synced from Nimble's version module. + + # Handling for special versions such as "#head" or "#branch". + if ver.isSpecial or ver2.isSpecial: + if ver2.isSpecial and ($ver2).normalize == "#head": + return ($ver).normalize != "#head" + + if not ver2.isSpecial: + # `#aa111 < 1.1` + return ($ver).normalize != "#head" + + # Handling for normal versions such as "0.1.0" or "1.0". + var sVer = string(ver).split('.') + var sVer2 = string(ver2).split('.') + for i in 0..max(sVer.len, sVer2.len)-1: + var sVerI = 0 + if i < sVer.len: + discard parseInt(sVer[i], sVerI) + var sVerI2 = 0 + if i < sVer2.len: + discard parseInt(sVer2[i], sVerI2) + if sVerI < sVerI2: + return true + elif sVerI == sVerI2: + discard + else: + return false proc addPackage(packages: StringTableRef, p: string) = let x = versionSplitPos(p) let name = p.substr(0, x-1) - let version = if x < p.len: p.substr(x+1) else: "" - if packages.getOrDefault(name) <. version: - packages[name] = version + let version = newVersion(if x < p.len: p.substr(x+1) else: "") + if packages.getOrDefault(name).newVersion < version or + (not packages.hasKey(name)): + packages[name] = $version iterator chosen(packages: StringTableRef): string = for key, val in pairs(packages): - let res = if val == latest: key else: key & '-' & val + let res = if val.len == 0: key else: key & '-' & val yield res proc addNimblePath(p: string, info: TLineInfo) = - if not contains(options.searchPaths, p): - message(info, hintPath, p) - options.lazyPaths.insert(p, 0) + var path = p + let nimbleLinks = toSeq(walkPattern(p / "*.nimble-link")) + if nimbleLinks.len > 0: + # If the user has more than one .nimble-link file then... we just ignore it. + # Spec for these files is available in Nimble's readme: + # https://github.com/nim-lang/nimble#nimble-link + let nimbleLinkLines = readFile(nimbleLinks[0]).splitLines() + path = nimbleLinkLines[1] + if not path.isAbsolute(): + path = p / path + + if not contains(options.searchPaths, path): + message(info, hintPath, path) + options.lazyPaths.insert(path, 0) proc addPathRec(dir: string, info: TLineInfo) = var packages = newStringTable(modeStyleInsensitive) @@ -82,7 +107,17 @@ proc nimblePath*(path: string, info: TLineInfo) = addNimblePath(path, info) when isMainModule: + proc v(s: string): Version = s.newVersion + # #head is special in the sense that it's assumed to always be newest. + doAssert v"1.0" < v"#head" + doAssert v"1.0" < v"1.1" + doAssert v"1.0.1" < v"1.1" + doAssert v"1" < v"1.1" + doAssert v"#aaaqwe" < v"1.1" # We cannot assume that a branch is newer. + doAssert v"#a111" < v"#head" + var rr = newStringTable() + addPackage rr, "irc-#a111" addPackage rr, "irc-#head" addPackage rr, "irc-0.1.0" addPackage rr, "irc" @@ -93,5 +128,6 @@ when isMainModule: addPackage rr, "ab-0.1" addPackage rr, "justone" - for p in rr.chosen: - echo p + doAssert toSeq(rr.chosen) == + @["irc-#head", "another-0.1", "ab-0.1.3", "justone"] + diff --git a/compiler/nimconf.nim b/compiler/nimconf.nim index 808159b8f..c19b41af1 100644 --- a/compiler/nimconf.nim +++ b/compiler/nimconf.nim @@ -152,7 +152,7 @@ proc confTok(L: var TLexer, tok: var TToken; config: ConfigRef) = parseDirective(L, tok, config) # else: give the token to the parser proc checkSymbol(L: TLexer, tok: TToken) = - if tok.tokType notin {tkSymbol..pred(tkIntLit), tkStrLit..tkTripleStrLit}: + if tok.tokType notin {tkSymbol..tkInt64Lit, tkStrLit..tkTripleStrLit}: lexMessage(L, errIdentifierExpected, tokToStr(tok)) proc parseAssignment(L: var TLexer, tok: var TToken; config: ConfigRef) = diff --git a/compiler/options.nim b/compiler/options.nim index bf1b04c2c..40d56aea5 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -94,7 +94,7 @@ type cmdRun # run the project via TCC backend TStringSeq* = seq[string] TGCMode* = enum # the selected GC - gcNone, gcBoehm, gcGo, gcStack, gcMarkAndSweep, gcRefc, + gcNone, gcBoehm, gcGo, gcRegions, gcMarkAndSweep, gcRefc, gcV2, gcGenerational IdeCmd* = enum @@ -291,8 +291,8 @@ proc pathSubs*(p, config: string): string = "projectpath", options.gProjectPath, "projectdir", options.gProjectPath, "nimcache", getNimcacheDir()]) - if '~' in result: - result = result.replace("~", home) + if "~/" in result: + result = result.replace("~/", home & '/') proc toGeneratedFile*(path, ext: string): string = ## converts "/home/a/mymodule.nim", "rod" to "/home/a/nimcache/mymodule.rod" diff --git a/compiler/parser.nim b/compiler/parser.nim index 3cd1e4d92..253716247 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -685,6 +685,11 @@ proc namedParams(p: var TParser, callee: PNode, # progress guaranteed exprColonEqExprListAux(p, endTok, result) +proc commandParam(p: var TParser): PNode = + result = parseExpr(p) + if p.tok.tokType == tkDo: + result = postExprBlocks(p, result) + proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode = #| primarySuffix = '(' (exprColonEqExpr comma?)* ')' doBlocks? #| | doBlocks @@ -733,7 +738,7 @@ proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode = when true: # progress NOT guaranteed p.hasProgress = false - addSon result, parseExpr(p) + addSon result, commandParam(p) if not p.hasProgress: break else: while p.tok.tokType != tkEof: @@ -1253,14 +1258,12 @@ proc parseExprStmt(p: var TParser): PNode = while true: getTok(p) optInd(p, result) - var e = parseExpr(p) - addSon(result, e) + addSon(result, commandParam(p)) if p.tok.tokType != tkComma: break elif p.tok.indent < 0 and isExprStart(p): result = newNode(nkCommand, a.info, @[a]) while true: - var e = parseExpr(p) - addSon(result, e) + addSon(result, commandParam(p)) if p.tok.tokType != tkComma: break getTok(p) optInd(p, result) diff --git a/compiler/passes.nim b/compiler/passes.nim index 7966ee88d..bf6ce1a0a 100644 --- a/compiler/passes.nim +++ b/compiler/passes.nim @@ -13,7 +13,7 @@ import strutils, options, ast, astalgo, llstream, msgs, platform, os, condsyms, idents, renderer, types, extccomp, math, magicsys, nversion, - nimsets, syntaxes, times, rodread, idgen, modulegraphs + nimsets, syntaxes, times, rodread, idgen, modulegraphs, reorder type TPassContext* = object of RootObj # the pass's context @@ -202,7 +202,7 @@ proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream, if graph.stopCompile(): break var n = parseTopLevelStmt(p) if n.kind == nkEmpty: break - if sfNoForward in module.flags: + if {sfNoForward, sfReorder} * module.flags != {}: # read everything, no streaming possible var sl = newNodeI(nkStmtList, n.info) sl.add n @@ -210,6 +210,8 @@ proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream, var n = parseTopLevelStmt(p) if n.kind == nkEmpty: break sl.add n + if sfReorder in module.flags: + sl = reorder sl discard processTopLevelStmt(sl, a) break elif not processTopLevelStmt(n, a): break diff --git a/compiler/platform.nim b/compiler/platform.nim index eb0aca186..01ddba23e 100644 --- a/compiler/platform.nim +++ b/compiler/platform.nim @@ -21,8 +21,8 @@ type # conditionals to condsyms (end of module). osNone, osDos, osWindows, osOs2, osLinux, osMorphos, osSkyos, osSolaris, osIrix, osNetbsd, osFreebsd, osOpenbsd, osDragonfly, osAix, osPalmos, osQnx, - osAmiga, osAtari, osNetware, osMacos, osMacosx, osHaiku, osVxworks, osGenode - osJS, osNimrodVM, osStandalone + osAmiga, osAtari, osNetware, osMacos, osMacosx, osHaiku, osAndroid, osVxworks + osGenode, osJS, osNimrodVM, osStandalone type TInfoOSProp* = enum @@ -143,6 +143,10 @@ const objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/", scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".", props: {ospNeedsPIC, ospPosix, ospLacksThreadVars}), + (name: "Android", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/", + objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/", + scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".", + props: {ospNeedsPIC, ospPosix}), (name: "VxWorks", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/", objExt: ".o", newLine: "\x0A", pathSep: ";", dirSep: "\\", scriptExt: ".sh", curDir: ".", exeExt: ".vxe", extSep: ".", @@ -171,7 +175,8 @@ type # alias conditionals to condsyms (end of module). cpuNone, cpuI386, cpuM68k, cpuAlpha, cpuPowerpc, cpuPowerpc64, cpuPowerpc64el, cpuSparc, cpuVm, cpuIa64, cpuAmd64, cpuMips, cpuMipsel, - cpuArm, cpuArm64, cpuJS, cpuNimrodVM, cpuAVR, cpuMSP430, cpuSparc64 + cpuArm, cpuArm64, cpuJS, cpuNimrodVM, cpuAVR, cpuMSP430, cpuSparc64, + cpuMips64, cpuMips64el type TEndian* = enum @@ -200,7 +205,9 @@ const (name: "nimrodvm", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32), (name: "avr", intSize: 16, endian: littleEndian, floatSize: 32, bit: 16), (name: "msp430", intSize: 16, endian: littleEndian, floatSize: 32, bit: 16), - (name: "sparc64", intSize: 64, endian: bigEndian, floatSize: 64, bit: 64)] + (name: "sparc64", intSize: 64, endian: bigEndian, floatSize: 64, bit: 64), + (name: "mips64", intSize: 64, endian: bigEndian, floatSize: 64, bit: 64), + (name: "mips64el", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64)] var targetCPU*, hostCPU*: TSystemCPU diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 7e1db5b29..bc3771700 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -45,7 +45,7 @@ const wFatal, wDefine, wUndef, wCompile, wLink, wLinksys, wPure, wPush, wPop, wBreakpoint, wWatchPoint, wPassl, wPassc, wDeadCodeElim, wDeprecated, wFloatchecks, wInfChecks, wNanChecks, wPragma, wEmit, wUnroll, - wLinearScanEnd, wPatterns, wEffects, wNoForward, wComputedGoto, + wLinearScanEnd, wPatterns, wEffects, wNoForward, wReorder, wComputedGoto, wInjectStmt, wDeprecated, wExperimental, wThis} lambdaPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl, wNosideeffect, wSideeffect, wNoreturn, wDynlib, wHeader, @@ -210,9 +210,9 @@ proc pragmaDeadCodeElim(c: PContext, n: PNode) = if isTurnedOn(c, n): incl(c.module.flags, sfDeadCodeElim) else: excl(c.module.flags, sfDeadCodeElim) -proc pragmaNoForward(c: PContext, n: PNode) = - if isTurnedOn(c, n): incl(c.module.flags, sfNoForward) - else: excl(c.module.flags, sfNoForward) +proc pragmaNoForward(c: PContext, n: PNode; flag=sfNoForward) = + if isTurnedOn(c, n): incl(c.module.flags, flag) + else: excl(c.module.flags, flag) proc processCallConv(c: PContext, n: PNode) = if (n.kind == nkExprColonExpr) and (n.sons[1].kind == nkIdent): @@ -726,6 +726,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int, incl(sym.flags, sfThread) of wDeadCodeElim: pragmaDeadCodeElim(c, it) of wNoForward: pragmaNoForward(c, it) + of wReorder: pragmaNoForward(c, it, sfReorder) of wMagic: processMagic(c, it, sym) of wCompileTime: noVal(it) diff --git a/compiler/renderer.nim b/compiler/renderer.nim index 7d9536625..220693f68 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -1327,7 +1327,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = if n.hasExplicitParams: put(g, tkBracketLe, "[") - gcomma(g, n) + gsemicolon(g, n) put(g, tkBracketRi, "]") of nkFormalParams: put(g, tkParLe, "(") diff --git a/compiler/reorder.nim b/compiler/reorder.nim new file mode 100644 index 000000000..a9ad1fd97 --- /dev/null +++ b/compiler/reorder.nim @@ -0,0 +1,102 @@ + +import intsets, tables, ast, idents, renderer + +const + nfTempMark = nfTransf + nfPermMark = nfNoRewrite + +proc accQuoted(n: PNode): PIdent = + var id = "" + for i in 0 .. <n.len: + let x = n[i] + case x.kind + of nkIdent: id.add(x.ident.s) + of nkSym: id.add(x.sym.name.s) + else: discard + result = getIdent(id) + +proc addDecl(n: PNode; declares: var IntSet) = + case n.kind + of nkPostfix: addDecl(n[1], declares) + of nkPragmaExpr: addDecl(n[0], declares) + of nkIdent: + declares.incl n.ident.id + of nkSym: + declares.incl n.sym.name.id + of nkAccQuoted: + declares.incl accQuoted(n).id + else: discard + +proc computeDeps(n: PNode, declares, uses: var IntSet; topLevel: bool) = + template deps(n) = computeDeps(n, declares, uses, false) + template decl(n) = + if topLevel: addDecl(n, declares) + case n.kind + of procDefs: + decl(n[0]) + for i in 1..bodyPos: deps(n[i]) + of nkLetSection, nkVarSection, nkUsingStmt: + for a in n: + if a.kind in {nkIdentDefs, nkVarTuple}: + for j in countup(0, a.len-3): decl(a[j]) + for j in a.len-2..a.len-1: deps(a[j]) + of nkConstSection, nkTypeSection: + for a in n: + if a.len >= 3: + decl(a[0]) + for i in 1..<a.len: deps(a[i]) + of nkIdent: uses.incl n.ident.id + of nkSym: uses.incl n.sym.name.id + of nkAccQuoted: uses.incl accQuoted(n).id + of nkOpenSymChoice, nkClosedSymChoice: + uses.incl n.sons[0].sym.name.id + of nkStmtList, nkStmtListExpr, nkWhenStmt, nkElifBranch, nkElse: + for i in 0..<len(n): computeDeps(n[i], declares, uses, topLevel) + else: + for i in 0..<safeLen(n): deps(n[i]) + +proc visit(i: int; all, res: PNode; deps: var seq[(IntSet, IntSet)]): bool = + let n = all[i] + if nfTempMark in n.flags: + # not a DAG! + return true + if nfPermMark notin n.flags: + incl n.flags, nfTempMark + var uses = deps[i][1] + for j in 0..<all.len: + if j != i: + let declares = deps[j][0] + for d in declares: + if uses.contains(d): + let oldLen = res.len + if visit(j, all, res, deps): + result = true + # rollback what we did, it turned out to be a dependency that caused + # trouble: + for k in oldLen..<res.len: + res.sons[k].flags = res.sons[k].flags - {nfPermMark, nfTempMark} + if oldLen != res.len: res.sons.setLen oldLen + break + n.flags = n.flags + {nfPermMark} - {nfTempMark} + res.add n + +proc reorder*(n: PNode): PNode = + result = newNodeI(nkStmtList, n.info) + var deps = newSeq[(IntSet, IntSet)](n.len) + for i in 0..<n.len: + deps[i][0] = initIntSet() + deps[i][1] = initIntSet() + computeDeps(n[i], deps[i][0], deps[i][1], true) + + for i in 0 .. n.len-1: + discard visit(i, n, result, deps) + for i in 0..<result.len: + result.sons[i].flags = result.sons[i].flags - {nfTempMark, nfPermMark} + when false: + # reverse the result: + let L = result.len-1 + for i in 0 .. result.len div 2: + result.sons[i].flags = result.sons[i].flags - {nfTempMark, nfPermMark} + result.sons[L - i].flags = result.sons[L - i].flags - {nfTempMark, nfPermMark} + swap(result.sons[i], result.sons[L - i]) + #echo result diff --git a/compiler/ropes.nim b/compiler/ropes.nim index d84b59f78..358ce8a53 100644 --- a/compiler/ropes.nim +++ b/compiler/ropes.nim @@ -228,6 +228,7 @@ proc prepend*(a: var Rope, b: string) = a = b & a var rnl* = tnl.newRope softRnl* = tnl.newRope + noRnl* = "".newRope proc `%`*(frmt: FormatStr, args: openArray[Rope]): Rope = var i = 0 diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 5f48e2fc5..74b074f61 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -47,11 +47,9 @@ proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = #raiseRecoverableError("") result = errorNode(c, n) if result.typ == nil or result.typ == enforceVoidContext: - if n.kind != nkStmtList: - # we cannot check for 'void' in macros ... - localError(n.info, errExprXHasNoType, - renderTree(result, {renderNoComments})) - result.typ = errorType(c) + localError(n.info, errExprXHasNoType, + renderTree(result, {renderNoComments})) + result.typ = errorType(c) else: if efNoProcvarCheck notin flags: semProcvarCheck(c, result) if result.typ.kind == tyVar: result = newDeref(result) @@ -276,41 +274,6 @@ proc semSizeof(c: PContext, n: PNode): PNode = n.typ = getSysType(tyInt) result = n -proc semOf(c: PContext, n: PNode): PNode = - if sonsLen(n) == 3: - n.sons[1] = semExprWithType(c, n.sons[1]) - n.sons[2] = semExprWithType(c, n.sons[2], {efDetermineType}) - #restoreOldStyleType(n.sons[1]) - #restoreOldStyleType(n.sons[2]) - let a = skipTypes(n.sons[1].typ, abstractPtrs) - let b = skipTypes(n.sons[2].typ, abstractPtrs) - let x = skipTypes(n.sons[1].typ, abstractPtrs-{tyTypeDesc}) - let y = skipTypes(n.sons[2].typ, abstractPtrs-{tyTypeDesc}) - - if x.kind == tyTypeDesc or y.kind != tyTypeDesc: - localError(n.info, errXExpectsObjectTypes, "of") - elif b.kind != tyObject or a.kind != tyObject: - localError(n.info, errXExpectsObjectTypes, "of") - else: - let diff = inheritanceDiff(a, b) - # | returns: 0 iff `a` == `b` - # | returns: -x iff `a` is the x'th direct superclass of `b` - # | returns: +x iff `a` is the x'th direct subclass of `b` - # | returns: `maxint` iff `a` and `b` are not compatible at all - if diff <= 0: - # optimize to true: - message(n.info, hintConditionAlwaysTrue, renderTree(n)) - result = newIntNode(nkIntLit, 1) - result.info = n.info - result.typ = getSysType(tyBool) - return result - elif diff == high(int): - localError(n.info, errXcanNeverBeOfThisSubtype, typeToString(a)) - else: - localError(n.info, errXExpectsTwoArguments, "of") - n.typ = getSysType(tyBool) - result = n - proc isOpImpl(c: PContext, n: PNode, flags: TExprFlags): PNode = internalAssert n.sonsLen == 3 and n[1].typ != nil and n[1].typ.kind == tyTypeDesc and @@ -1121,9 +1084,11 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = if ty.n != nil and ty.n.kind == nkRecList: let field = lookupInRecord(ty.n, i) if field != nil: - n.typ = newTypeWithSons(c, tyFieldAccessor, @[ty, field.typ]) - n.typ.n = copyTree(n) + n.typ = makeTypeDesc(c, field.typ) return n + #n.typ = newTypeWithSons(c, tyFieldAccessor, @[ty, field.typ]) + #n.typ.n = copyTree(n) + #return n else: tryReadingGenericParam(ty) return @@ -1490,6 +1455,9 @@ proc semYieldVarResult(c: PContext, n: PNode, restype: PType) = var t = skipTypes(restype, {tyGenericInst, tyAlias}) case t.kind of tyVar: + if n.sons[0].kind in {nkHiddenStdConv, nkHiddenSubConv}: + n.sons[0] = n.sons[0].sons[1] + n.sons[0] = takeImplicitAddr(c, n.sons[0]) of tyTuple: for i in 0.. <t.sonsLen: @@ -1829,11 +1797,11 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = of mDefined: result = semDefined(c, setMs(n, s), false) of mDefinedInScope: result = semDefined(c, setMs(n, s), true) of mCompiles: result = semCompiles(c, setMs(n, s), flags) - of mLow: result = semLowHigh(c, setMs(n, s), mLow) - of mHigh: result = semLowHigh(c, setMs(n, s), mHigh) + #of mLow: result = semLowHigh(c, setMs(n, s), mLow) + #of mHigh: result = semLowHigh(c, setMs(n, s), mHigh) of mSizeOf: result = semSizeof(c, setMs(n, s)) of mIs: result = semIs(c, setMs(n, s), flags) - of mOf: result = semOf(c, setMs(n, s)) + #of mOf: result = semOf(c, setMs(n, s)) of mShallowCopy: result = semShallowCopy(c, n, flags) of mExpandToAst: result = semExpandToAst(c, n, s, flags) of mQuoteAst: result = semQuoteAst(c, n) @@ -2207,9 +2175,14 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = message(n.info, warnDeprecated, "bind") result = semExpr(c, n.sons[0], flags) of nkTypeOfExpr, nkTupleTy, nkTupleClassTy, nkRefTy..nkEnumTy, nkStaticTy: + if c.matchedConcept != nil and n.len == 1: + let modifier = n.modifierTypeKindOfNode + if modifier != tyNone: + var baseType = semExpr(c, n[0]).typ.skipTypes({tyTypeDesc}) + result.typ = c.makeTypeDesc(c.newTypeWithSons(modifier, @[baseType])) + return var typ = semTypeNode(c, n, nil).skipTypes({tyTypeDesc}) result.typ = makeTypeDesc(c, typ) - #result = symNodeFromType(c, typ, n.info) of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit: # check if it is an expression macro: checkMinSonsLen(n, 1) diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index c664f735c..8b3d9c014 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -200,6 +200,41 @@ proc isStrangeArray(t: PType): bool = let t = t.skipTypes(abstractInst) result = t.kind == tyArray and t.firstOrd != 0 +proc semOf(c: PContext, n: PNode): PNode = + if sonsLen(n) == 3: + n.sons[1] = semExprWithType(c, n.sons[1]) + n.sons[2] = semExprWithType(c, n.sons[2], {efDetermineType}) + #restoreOldStyleType(n.sons[1]) + #restoreOldStyleType(n.sons[2]) + let a = skipTypes(n.sons[1].typ, abstractPtrs) + let b = skipTypes(n.sons[2].typ, abstractPtrs) + let x = skipTypes(n.sons[1].typ, abstractPtrs-{tyTypeDesc}) + let y = skipTypes(n.sons[2].typ, abstractPtrs-{tyTypeDesc}) + + if x.kind == tyTypeDesc or y.kind != tyTypeDesc: + localError(n.info, errXExpectsObjectTypes, "of") + elif b.kind != tyObject or a.kind != tyObject: + localError(n.info, errXExpectsObjectTypes, "of") + else: + let diff = inheritanceDiff(a, b) + # | returns: 0 iff `a` == `b` + # | returns: -x iff `a` is the x'th direct superclass of `b` + # | returns: +x iff `a` is the x'th direct subclass of `b` + # | returns: `maxint` iff `a` and `b` are not compatible at all + if diff <= 0: + # optimize to true: + message(n.info, hintConditionAlwaysTrue, renderTree(n)) + result = newIntNode(nkIntLit, 1) + result.info = n.info + result.typ = getSysType(tyBool) + return result + elif diff == high(int): + localError(n.info, errXcanNeverBeOfThisSubtype, typeToString(a)) + else: + localError(n.info, errXExpectsTwoArguments, "of") + n.typ = getSysType(tyBool) + result = n + proc magicsAfterOverloadResolution(c: PContext, n: PNode, flags: TExprFlags): PNode = case n[0].sym.magic @@ -219,6 +254,7 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode, result.typ = getSysType(tyString) of mInstantiationInfo: result = semInstantiationInfo(c, n) of mOrd: result = semOrd(c, n) + of mOf: result = semOf(c, n) of mHigh, mLow: result = semLowHigh(c, n, n[0].sym.magic) of mShallowCopy: result = semShallowCopy(c, n, flags) of mNBindSym: result = semBindSym(c, n) diff --git a/compiler/semobjconstr.nim b/compiler/semobjconstr.nim index f4c225526..b331d05a1 100644 --- a/compiler/semobjconstr.nim +++ b/compiler/semobjconstr.nim @@ -44,7 +44,9 @@ proc locateFieldInInitExpr(field: PSym, initExpr: PNode): PNode = let fieldId = field.name.id for i in 1 .. <initExpr.len: let assignment = initExpr[i] - internalAssert assignment.kind == nkExprColonExpr + if assignment.kind != nkExprColonExpr: + localError(initExpr.info, "incorrect object construction syntax") + continue if fieldId == considerQuotedIdent(assignment[0]).id: return assignment @@ -278,6 +280,9 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = for i in 1.. <result.len: let field = result[i] if nfSem notin field.flags: + if field.kind != nkExprColonExpr: + localError(n.info, "incorrect object construction syntax") + continue let id = considerQuotedIdent(field[0]) # This node was not processed. There are two possible reasons: # 1) It was shadowed by a field with the same name on the left diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index 5ac2e678a..8ad8a6288 100644 --- a/compiler/semtempl.nim +++ b/compiler/semtempl.nim @@ -12,14 +12,14 @@ discard """ hygienic templates: - template `||` (a, b: expr): expr = + template `||` (a, b: untyped): untyped = let aa = a if aa: aa else: b var a, b: T - a || b || a + echo a || b || a Each evaluation context has to be different and we need to perform some form of preliminary symbol lookup in template definitions. Hygiene is diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index de71f1632..a7c9244cc 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -1202,6 +1202,15 @@ proc freshType(res, prev: PType): PType {.inline.} = else: result = res +template modifierTypeKindOfNode(n: PNode): TTypeKind = + case n.kind + of nkVarTy: tyVar + of nkRefTy: tyRef + of nkPtrTy: tyPtr + of nkStaticTy: tyStatic + of nkTypeOfExpr: tyTypeDesc + else: tyNone + proc semTypeClass(c: PContext, n: PNode, prev: PType): PType = # if n.sonsLen == 0: return newConstraint(c, tyTypeClass) if nfBase2 in n.flags: @@ -1227,13 +1236,7 @@ proc semTypeClass(c: PContext, n: PNode, prev: PType): PType = dummyName: PNode dummyType: PType - let modifier = case param.kind - of nkVarTy: tyVar - of nkRefTy: tyRef - of nkPtrTy: tyPtr - of nkStaticTy: tyStatic - of nkTypeOfExpr: tyTypeDesc - else: tyNone + let modifier = param.modifierTypeKindOfNode if modifier != tyNone: dummyName = param[0] @@ -1509,9 +1512,26 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = dec c.inTypeContext proc setMagicType(m: PSym, kind: TTypeKind, size: int) = + # source : https://en.wikipedia.org/wiki/Data_structure_alignment#x86 m.typ.kind = kind - m.typ.align = size.int16 m.typ.size = size + # this usually works for most basic types + # Assuming that since ARM, ARM64 don't support unaligned access + # data is aligned to type size + m.typ.align = size.int16 + + # FIXME: proper support for clongdouble should be added. + # long double size can be 8, 10, 12, 16 bytes depending on platform & compiler + if targetCPU == cpuI386 and size == 8: + #on Linux/BSD i386, double are aligned to 4bytes (except with -malign-double) + if kind in {tyFloat64, tyFloat} and + targetOS in {osLinux, osAndroid, osNetbsd, osFreebsd, osOpenbsd, osDragonfly}: + m.typ.align = 4 + # on i386, all known compiler, 64bits ints are aligned to 4bytes (except with -malign-double) + elif kind in {tyInt, tyUInt, tyInt64, tyUInt64}: + m.typ.align = 4 + else: + discard proc processMagicType(c: PContext, m: PSym) = case m.magic diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 6084e11c0..41596f05c 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -297,7 +297,9 @@ proc describeArgs*(c: PContext, n: PNode, startIdx = 1; n.sons[i].typ = arg.typ n.sons[i].sons[1] = arg else: - if arg.typ.isNil and arg.kind notin {nkStmtList, nkDo}: + if arg.typ.isNil and arg.kind notin {nkStmtList, nkDo, nkElse, + nkOfBranch, nkElifBranch, + nkExceptBranch}: arg = c.semOperand(c, n.sons[i]) n.sons[i] = arg if arg.typ != nil and arg.typ.kind == tyError: return diff --git a/compiler/testability.nim b/compiler/testability.nim deleted file mode 100644 index 4587a5344..000000000 --- a/compiler/testability.nim +++ /dev/null @@ -1,5 +0,0 @@ -template tests*(body: stmt) {.immediate.} = - when defined(selftest): - when not declared(unittest): import unittest - body - diff --git a/compiler/vm.nim b/compiler/vm.nim index b8e6467b5..93cf66c05 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -409,6 +409,28 @@ proc recSetFlagIsRef(arg: PNode) = for i in 0 ..< arg.safeLen: arg.sons[i].recSetFlagIsRef +proc setLenSeq(c: PCtx; node: PNode; newLen: int; info: TLineInfo) = + # FIXME: this doesn't attempt to solve incomplete + # support of tyPtr, tyRef in VM. + let typ = node.typ.skipTypes(abstractInst+{tyRange}-{tyTypeDesc}) + let typeEntry = typ.sons[0].skipTypes(abstractInst+{tyRange}-{tyTypeDesc}) + let typeKind = case typeEntry.kind + of tyUInt..tyUInt64: nkUIntLit + of tyRange, tyEnum, tyBool, tyChar, tyInt..tyInt64: nkIntLit + of tyFloat..tyFloat128: nkFloatLit + of tyString: nkStrLit + of tyObject: nkObjConstr + of tySequence: nkNilLit + of tyProc, tyTuple: nkPar + else: nkEmpty + + let oldLen = node.len + setLen(node.sons, newLen) + if oldLen < newLen: + # TODO: This is still not correct for tyPtr, tyRef default value + for i in oldLen .. <newLen: + node.sons[i] = newNodeI(typeKind, info) + proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = var pc = start var tos = tos @@ -1118,14 +1140,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = decodeB(rkNode) let newLen = regs[rb].intVal.int if regs[ra].node.isNil: stackTrace(c, tos, pc, errNilAccess) - else: - let oldLen = regs[ra].node.len - setLen(regs[ra].node.sons, newLen) - if oldLen < newLen: - # XXX This is still not entirely correct - # set to default value: - for i in oldLen .. <newLen: - regs[ra].node.sons[i] = newNodeI(nkEmpty, c.debug[pc]) + else: c.setLenSeq(regs[ra].node, newLen, c.debug[pc]) of opcReset: internalError(c.debug[pc], "too implement") of opcNarrowS: @@ -1307,12 +1322,24 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = ensureKind(rkNode) if c.callsite != nil: regs[ra].node = c.callsite else: stackTrace(c, tos, pc, errFieldXNotFound, "callsite") - of opcNLineInfo: + of opcNGetFile: decodeB(rkNode) let n = regs[rb].node - createStr regs[ra] - regs[ra].node.strVal = n.info.toFileLineCol - regs[ra].node.info = c.debug[pc] + regs[ra].node = newStrNode(nkStrLit, n.info.toFilename) + regs[ra].node.info = n.info + regs[ra].node.typ = n.typ + of opcNGetLine: + decodeB(rkNode) + let n = regs[rb].node + regs[ra].node = newIntNode(nkIntLit, n.info.line) + regs[ra].node.info = n.info + regs[ra].node.typ = n.typ + of opcNGetColumn: + decodeB(rkNode) + let n = regs[rb].node + regs[ra].node = newIntNode(nkIntLit, n.info.col) + regs[ra].node.info = n.info + regs[ra].node.typ = n.typ of opcEqIdent: decodeBC(rkInt) if regs[rb].node.kind == nkIdent and regs[rc].node.kind == nkIdent: @@ -1471,6 +1498,17 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = createStrKeepNode(regs[ra]) if regs[ra].node.strVal.isNil: regs[ra].node.strVal = newStringOfCap(1000) storeAny(regs[ra].node.strVal, typ, regs[rb].regToNode) + of opcToNarrowInt: + decodeBC(rkInt) + let mask = (1'i64 shl rc) - 1 # 0xFF + let signbit = 1'i64 shl (rc - 1) # 0x80 + let toggle = mask - signbit # 0x7F + # algorithm: -((i8 and 0xFF) xor 0x7F) + 0x7F + # mask off higher bits. + # uses two's complement to sign-extend integer. + # reajust integer into desired range. + regs[ra].intVal = -((regs[rb].intVal and mask) xor toggle) + toggle + inc pc proc execute(c: PCtx, start: int): PNode = diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim index 263ec8378..7e1309e0a 100644 --- a/compiler/vmdef.nim +++ b/compiler/vmdef.nim @@ -98,7 +98,7 @@ type opcNError, opcNWarning, opcNHint, - opcNLineInfo, + opcNGetLine, opcNGetColumn, opcNGetFile, opcEqIdent, opcStrToIdent, opcIdentToStr, @@ -136,7 +136,8 @@ type opcNBindSym, opcSetType, # dest.typ = types[Bx] opcTypeTrait, - opcMarshalLoad, opcMarshalStore + opcMarshalLoad, opcMarshalStore, + opcToNarrowInt TBlock* = object label*: PSym diff --git a/compiler/vmdeps.nim b/compiler/vmdeps.nim index b2b1ec92b..b9bbba551 100644 --- a/compiler/vmdeps.nim +++ b/compiler/vmdeps.nim @@ -224,12 +224,13 @@ proc mapTypeToAstX(t: PType; info: TLineInfo; result.add copyTree(c) of tyTuple: if inst: - result = newNodeX(nkTupleTy) # only named tuples have a node, unnamed tuples don't if t.n.isNil: + result = newNodeX(nkPar) for subType in t.sons: result.add mapTypeToAst(subType, info) else: + result = newNodeX(nkTupleTy) for s in t.n.sons: result.add newIdentDefs(s) else: diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index ba89f88d4..dbb8c9dcd 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -656,16 +656,17 @@ proc genNarrow(c: PCtx; n: PNode; dest: TDest) = let t = skipTypes(n.typ, abstractVar-{tyTypeDesc}) # uint is uint64 in the VM, we we only need to mask the result for # other unsigned types: - if t.kind in {tyUInt8..tyUInt32}: + if t.kind in {tyUInt8..tyUInt32} or (t.kind == tyUInt and t.size < 8): c.gABC(n, opcNarrowU, dest, TRegister(t.size*8)) - elif t.kind in {tyInt8..tyInt32}: + elif t.kind in {tyInt8..tyInt32} or (t.kind == tyInt and t.size < 8): c.gABC(n, opcNarrowS, dest, TRegister(t.size*8)) proc genNarrowU(c: PCtx; n: PNode; dest: TDest) = let t = skipTypes(n.typ, abstractVar-{tyTypeDesc}) # uint is uint64 in the VM, we we only need to mask the result for # other unsigned types: - if t.kind in {tyUInt8..tyUInt32, tyInt8..tyInt32}: + if t.kind in {tyUInt8..tyUInt32, tyInt8..tyInt32} or + (t.kind in {tyUInt, tyInt} and t.size < 8): c.gABC(n, opcNarrowU, dest, TRegister(t.size*8)) proc genBinaryABCnarrow(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) = @@ -875,11 +876,25 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = of mBitnotI: genUnaryABC(c, n, dest, opcBitnotInt) genNarrowU(c, n, dest) - of mZe8ToI, mZe8ToI64, mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64, - mToU8, mToU16, mToU32, mToFloat, mToBiggestFloat, mToInt, + of mToFloat, mToBiggestFloat, mToInt, mToBiggestInt, mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr: genConv(c, n, n.sons[1], dest) + of mZe8ToI, mZe8ToI64, mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64: + #genNarrowU modified + let t = skipTypes(n.sons[1].typ, abstractVar-{tyTypeDesc}) + let tmp = c.genx(n.sons[1]) + c.gABC(n, opcNarrowU, tmp, TRegister(t.size*8)) + # assign result to dest register + if dest < 0: dest = c.getTemp(n.typ) + c.gABC(n, opcAsgnInt, dest, tmp) + c.freeTemp(tmp) + of mToU8, mToU16, mToU32: + let t = skipTypes(n.typ, abstractVar-{tyTypeDesc}) + var tmp = c.genx(n.sons[1]) + if dest < 0: dest = c.getTemp(n.typ) + c.gABC(n, opcToNarrowInt, dest, tmp, TRegister(t.size*8)) + c.freeTemp(tmp) of mEqStr, mEqCString: genBinaryABC(c, n, dest, opcEqStr) of mLeStr: genBinaryABC(c, n, dest, opcLeStr) of mLtStr: genBinaryABC(c, n, dest, opcLtStr) @@ -1071,7 +1086,16 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = of mEqIdent: genBinaryABC(c, n, dest, opcEqIdent) of mEqNimrodNode: genBinaryABC(c, n, dest, opcEqNimrodNode) of mSameNodeType: genBinaryABC(c, n, dest, opcSameNodeType) - of mNLineInfo: genUnaryABC(c, n, dest, opcNLineInfo) + of mNLineInfo: + case n[0].sym.name.s + of "getFile": + genUnaryABC(c, n, dest, opcNGetFile) + of "getLine": + genUnaryABC(c, n, dest, opcNGetLine) + of "getColumn": + genUnaryABC(c, n, dest, opcNGetColumn) + else: + internalAssert false of mNHint: unused(n, dest) genUnaryStmt(c, n, opcNHint) diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim index 98fd912d8..773ab8ff5 100644 --- a/compiler/wordrecg.nim +++ b/compiler/wordrecg.nim @@ -55,7 +55,7 @@ type wFloatchecks, wNanChecks, wInfChecks, wAssertions, wPatterns, wWarnings, wHints, wOptimization, wRaises, wWrites, wReads, wSize, wEffects, wTags, - wDeadCodeElim, wSafecode, wNoForward, wNoRewrite, + wDeadCodeElim, wSafecode, wNoForward, wReorder, wNoRewrite, wPragma, wCompileTime, wNoInit, wPassc, wPassl, wBorrow, wDiscardable, @@ -143,7 +143,7 @@ const "assertions", "patterns", "warnings", "hints", "optimization", "raises", "writes", "reads", "size", "effects", "tags", - "deadcodeelim", "safecode", "noforward", "norewrite", + "deadcodeelim", "safecode", "noforward", "reorder", "norewrite", "pragma", "compiletime", "noinit", "passc", "passl", "borrow", "discardable", "fieldchecks", diff --git a/config/nim.cfg b/config/nim.cfg index 9374e2b88..7ac87cc33 100644 --- a/config/nim.cfg +++ b/config/nim.cfg @@ -90,6 +90,18 @@ path="$lib/pure" @end @end +@if android: + cc = clang + @if termux: + gcc.options.linker = "-landroid-glob" + gcc.cpp.options.linker = "-landroid-glob" + clang.options.linker = "-landroid-glob" + clang.cpp.options.linker = "-landroid-glob" + tcc.options.linker = "-landroid-glob" + define:"useShPath:/system/bin/sh" + @end +@end + # Configuration for the Intel C/C++ compiler: @if windows: icl.options.speed = "/Ox /arch:SSE2" @@ -109,6 +121,9 @@ path="$lib/pure" tlsEmulation:on gcc.options.always = "-w" gcc.cpp.options.always = "-w -fpermissive" +@elif windows: + gcc.options.always = "-w -mno-ms-bitfields" + gcc.cpp.options.always = "-w -fpermissive -mno-ms-bitfields" @else: gcc.options.always = "-w" gcc.cpp.options.always = "-w -fpermissive" @@ -180,27 +195,39 @@ clang.options.speed = "-O3" clang.options.size = "-Os" # Configuration for the Visual C/C++ compiler: -vcc.exe = "vccexe.exe" -vcc.linkerexe = "vccexe.exe" +vcc.exe = "vccexe.exe" +vcc.cpp.exe = "vccexe.exe" +vcc.linkerexe = "vccexe.exe" +vcc.cpp.linkerexe = "vccexe.exe" # set the options for specific platforms: +vcc.options.always = "/nologo" +vcc.cpp.options.always %= "${vcc.options.always} /EHsc" +vcc.options.linker = "/nologo /DEBUG /Zi /F33554432" # set the stack size to 32 MiB +vcc.cpp.options.linker %= "${vcc.options.linker}" @if i386: -vcc.options.always = "--platform:x86 /nologo" -vcc.options.linker = "--platform:x86 /nologo /DEBUG /Zi /F33554432" # set the stack size to 32 MiB +vcc.options.always %= "--platform:x86 ${vcc.options.always}" +vcc.cpp.options.always %= "--platform:x86 ${vcc.cpp.options.always}" +vcc.options.linker %= "--platform:x86 ${vcc.options.linker}" +vcc.cpp.options.linker %= "--platform:x86 ${vcc.cpp.options.linker}" @elif amd64: -vcc.options.always = "--platform:amd64 /nologo" -vcc.options.linker = "--platform:amd64 /nologo /DEBUG /Zi /F33554432" # set the stack size to 32 MiB +vcc.options.always %= "--platform:amd64 ${vcc.options.always}" +vcc.cpp.options.always %= "--platform:amd64 ${vcc.cpp.options.always}" +vcc.options.linker %= "--platform:amd64 ${vcc.options.linker}" +vcc.cpp.options.linker %= "--platform:amd64 ${vcc.cpp.options.linker}" @elif arm: -vcc.options.always = "--platform:arm /nologo" -vcc.options.linker = "--platform:arm /nologo /DEBUG /Zi /F33554432" # set the stack size to 32 MiB -@else: -vcc.options.always = "/nologo" -vcc.options.linker = "/nologo /DEBUG /Zi /F33554432" # set the stack size to 32 MiB +vcc.options.always %= "--platform:arm ${vcc.options.always}" +vcc.cpp.options.always %= "--platform:arm ${vcc.cpp.options.always}" +vcc.options.linker %= "--platform:arm ${vcc.options.linker}" +vcc.cpp.options.linker %= "--platform:arm ${vcc.cpp.options.linker}" @end -vcc.options.debug = "/Zi /FS /Od" -vcc.options.speed = "/O2" -vcc.options.size = "/O1" +vcc.options.debug = "/Zi /FS /Od" +vcc.cpp.options.debug = "/Zi /FS /Od" +vcc.options.speed = "/O2" +vcc.cpp.options.speed = "/O2" +vcc.options.size = "/O1" +vcc.cpp.options.size = "/O1" # Configuration for the Tiny C Compiler: tcc.options.always = "-w" diff --git a/doc/advopt.txt b/doc/advopt.txt index b88e5f063..fb6fd719b 100644 --- a/doc/advopt.txt +++ b/doc/advopt.txt @@ -65,7 +65,7 @@ Advanced options: --skipUserCfg do not read the user's configuration file --skipParentCfg do not read the parent dirs' configuration files --skipProjCfg do not read the project's configuration file - --gc:refc|v2|markAndSweep|boehm|go|none + --gc:refc|v2|markAndSweep|boehm|go|none|regions select the GC to use; default is 'refc' --index:on|off turn index file generation on|off --putenv:key=value set an environment variable diff --git a/doc/lib.rst b/doc/lib.rst index ea43c0db9..64c3c11eb 100644 --- a/doc/lib.rst +++ b/doc/lib.rst @@ -539,9 +539,6 @@ Network Programming and Internet Protocols * `joyent_http_parser <joyent_http_parser.html>`_ Wrapper for the joyent's high-performance HTTP parser. -* `libcurl <libcurl.html>`_ - Wrapper for the libcurl library. - * `openssl <openssl.html>`_ Wrapper for OpenSSL. diff --git a/doc/manual/ffi.txt b/doc/manual/ffi.txt index e9b52eaca..06fed1430 100644 --- a/doc/manual/ffi.txt +++ b/doc/manual/ffi.txt @@ -132,7 +132,7 @@ translated into a C array of undetermined size: .. code-block:: nim type - ArrayPart{.unchecked.} = array[0..0, int] + ArrayPart{.unchecked.} = array[0, int] MySeq = object len, cap: int data: ArrayPart @@ -146,10 +146,6 @@ Produces roughly this C code: NI data[]; } MySeq; -The bounds checking done at compile time is not disabled for now, so to access -``s.data[C]`` (where ``C`` is a constant) the array's index needs to -include ``C``. - The base type of the unchecked array may not contain any GC'ed memory but this is currently not checked. diff --git a/doc/manual/lexing.txt b/doc/manual/lexing.txt index d4c11adf7..3147dd97c 100644 --- a/doc/manual/lexing.txt +++ b/doc/manual/lexing.txt @@ -130,7 +130,7 @@ Two identifiers are considered equal if the following algorithm returns true: .. code-block:: nim proc sameIdentifier(a, b: string): bool = a[0] == b[0] and - a.replace(re"_|–", "").toLower == b.replace(re"_|–", "").toLower + a.replace("_", "").toLower == b.replace("_", "").toLower That means only the first letters are compared in a case sensitive manner. Other letters are compared case insensitively and underscores are ignored. diff --git a/doc/manual/procs.txt b/doc/manual/procs.txt index 5f4c9f2fa..6d09ac3f5 100644 --- a/doc/manual/procs.txt +++ b/doc/manual/procs.txt @@ -248,12 +248,20 @@ calls can use the ``do`` keyword: .. code-block:: nim sort(cities) do (x,y: string) -> int: cmp(x.len, y.len) + # Less parenthesis using the method plus command syntax: cities = cities.map do (x:string) -> string: "City of " & x + # In macros, the do notation is often used for quasi-quoting + macroResults.add quote do: + if not `ex`: + echo `info`, ": Check failed: ", `expString` + ``do`` is written after the parentheses enclosing the regular proc params. The proc expression represented by the do block is appended to them. +In calls using the command syntax, the do block will bind to the immediately +preceeding expression, transforming it in a call. ``do`` with parentheses is an anonymous ``proc``; however a ``do`` without parentheses is just a block of code. The ``do`` notation can be used to @@ -275,8 +283,8 @@ Nonoverloadable builtins The following builtin procs cannot be overloaded for reasons of implementation simplicity (they require specialized semantic checking):: - declared, defined, definedInScope, compiles, low, high, sizeOf, - is, of, shallowCopy, getAst, astToStr, spawn, procCall + declared, defined, definedInScope, compiles, sizeOf, + is, shallowCopy, getAst, astToStr, spawn, procCall Thus they act more like keywords than like ordinary identifiers; unlike a keyword however, a redefinition may `shadow`:idx: the definition in diff --git a/doc/nimc.rst b/doc/nimc.rst index 5d9ed03ab..e949df69c 100644 --- a/doc/nimc.rst +++ b/doc/nimc.rst @@ -189,12 +189,28 @@ resides in its own directory so that the generated ``nimcache`` directory is not shared between different projects. +Compiler Selection +================== + +To change the compiler from the default compiler (at the command line):: + + nim c --cc:llvm_gcc --compile_only myfile.nim + +This uses the configuration defined in ``config\nim.cfg`` for ``lvm_gcc``. + +If nimcache already contains compiled code from a different compiler for the same project, +add the ``-f`` flag to force all files to be recompiled. + +The default compiler is defined at the top of ``config\nim.cfg``. Changing this setting +affects the compiler used by ``koch`` to (re)build Nim. + + Cross compilation ================= To cross compile, use for example:: - nim c --cpu:i386 --os:linux --compile_only --gen_script myproject.nim + nim c --cpu:i386 --os:linux --compileOnly --genScript myproject.nim Then move the C code and the compile script ``compile_myproject.sh`` to your Linux i386 machine and run the script. @@ -262,6 +278,15 @@ Define Effect what's in the Nim file with what's in the C header (requires a C compiler with _Static_assert support, like any C11 compiler) +``tempDir`` This symbol takes a string as its value, like + ``--define:tempDir:/some/temp/path`` to override the + temporary directory returned by ``os.getTempDir()``. + The value **should** end with a directory separator + character. (Relevant for the Android platform) +``useShPath`` This symbol takes a string as its value, like + ``--define:useShPath:/opt/sh/bin/sh`` to override the + path for the ``sh`` binary, in cases where it is not + located in the default location ``/bin/sh`` ================== ========================================================= diff --git a/doc/tut1.rst b/doc/tut1.rst index fc8e411cb..89893a39a 100644 --- a/doc/tut1.rst +++ b/doc/tut1.rst @@ -138,7 +138,7 @@ comments can also be nested. ]# ]# -You can also use the `discard statement`_ together with *long string +You can also use the `discard statement <#procedures-discard-statement>`_ together with *long string literals* to create block comments: .. code-block:: nim @@ -364,8 +364,7 @@ iterator: echo i # --> Outputs 1 2 3 4 5 6 7 8 9 10 on different lines -The built-in `$ <system.html#$>`_ operator turns an integer (``int``) and many -other types into a string. The variable ``i`` is implicitly declared by the +The variable ``i`` is implicitly declared by the ``for`` loop and has the type ``int``, because that is what `countup <system.html#countup>`_ returns. ``i`` runs through the values 1, 2, .., 10. Each value is ``echo``-ed. This code does the same: @@ -501,10 +500,6 @@ differences: The ``when`` statement is useful for writing platform specific code, similar to the ``#ifdef`` construct in the C programming language. -**Note**: To comment out a large piece of code, it is often better to use a -``when false:`` statement than to use real comments. This way nesting is -possible. - Statements and indentation ========================== diff --git a/doc/tut2.rst b/doc/tut2.rst index f145528a1..0bb4c94e1 100644 --- a/doc/tut2.rst +++ b/doc/tut2.rst @@ -218,7 +218,7 @@ So "pure object oriented" code is easy to write: import strutils, sequtils stdout.writeLine("Give a list of numbers (separated by spaces): ") - stdout.write(stdin.readLine.split.map(parseInt).max.`$`) + stdout.write(stdin.readLine.splitWhitespace.map(parseInt).max.`$`) stdout.writeLine(" is the maximum!") @@ -233,15 +233,15 @@ is needed: type Socket* = ref object of RootObj - host: int # cannot be accessed from the outside of the module due to missing star + h: int # cannot be accessed from the outside of the module due to missing star proc `host=`*(s: var Socket, value: int) {.inline.} = ## setter of host address - s.host = value + s.h = value proc host*(s: Socket): int {.inline.} = ## getter of host address - s.host + s.h var s: Socket new s @@ -723,7 +723,7 @@ regular expressions: .. code-block:: nim - macro case_token(n: typed): typed = + macro case_token(n: varargs[untyped]): typed = # creates a lexical analyzer from regular expressions # ... (implementation is an exercise for the reader :-) discard diff --git a/examples/allany.nim b/examples/allany.nim index 52a794204..6ff055aa6 100644 --- a/examples/allany.nim +++ b/examples/allany.nim @@ -1,6 +1,6 @@ # All and any -template all(container, cond: expr): expr {.immediate.} = +template all(container, cond: untyped): bool = block: var result = true for it in items(container): @@ -9,7 +9,7 @@ template all(container, cond: expr): expr {.immediate.} = break result -template any(container, cond: expr): expr {.immediate.} = +template any(container, cond: untyped): bool = block: var result = false for it in items(container): diff --git a/koch.nim b/koch.nim index aaa03d558..6ae45fcb7 100644 --- a/koch.nim +++ b/koch.nim @@ -227,14 +227,15 @@ proc bundleWinTools() = removeFile("tools/finish".exe) buildVccTool() nimexec("c -o:bin/nimgrab.exe -d:ssl tools/nimgrab.nim") + nimexec("c -o:bin/nimgrep.exe tools/nimgrep.nim") when false: # not yet a tool worth including nimexec(r"c --cc:vcc --app:gui -o:bin\downloader.exe -d:ssl --noNimblePath " & r"--path:..\ui tools\downloader.nim") proc zip(args: string) = - bundleNimbleSrc() - bundleNimsuggest(false) + bundleNimbleExe() + bundleNimsuggest(true) bundleWinTools() nimexec("cc -r $2 --var:version=$1 --var:mingw=none --main:compiler/nim.nim scripts compiler/installer.ini" % [VersionAsString, compileNimInst]) @@ -407,13 +408,15 @@ proc winReleaseArch(arch: string) = withMingw r"..\mingw" & arch & r"\bin": # Rebuilding koch is necessary because it uses its pointer size to # determine which mingw link to put in the NSIS installer. - nimexec "c --out:koch_temp --cpu:$# koch" % cpu - exec "koch_temp boot -d:release --cpu:$#" % cpu - exec "koch_temp zip -d:release" + nimexec "c --cpu:$# koch" % cpu + exec "koch boot -d:release --cpu:$#" % cpu + exec "koch zip -d:release" overwriteFile r"build\nim-$#.zip" % VersionAsString, r"web\upload\download\nim-$#_x$#.zip" % [VersionAsString, arch] -proc winRelease() = +proc winRelease*() = + # Now used from "tools/winrelease" and not directly supported by koch + # anymore! # Build -docs file: when true: web(gaCode) @@ -530,39 +533,40 @@ proc showHelp() = quit(HelpText % [VersionAsString & spaces(44-len(VersionAsString)), CompileDate, CompileTime], QuitSuccess) -var op = initOptParser() -op.next() -case op.kind -of cmdLongOption, cmdShortOption: showHelp() -of cmdArgument: - case normalize(op.key) - of "boot": boot(op.cmdLineRest) - of "clean": clean(op.cmdLineRest) - of "web": web(op.cmdLineRest) - of "doc", "docs": web("--onlyDocs " & op.cmdLineRest) - of "json2": web("--json2 " & op.cmdLineRest) - of "website": website(op.cmdLineRest & gaCode) - of "web0": - # undocumented command for Araq-the-merciful: - web(op.cmdLineRest & gaCode) - of "pdf": pdf() - of "csource", "csources": csource(op.cmdLineRest) - of "zip": zip(op.cmdLineRest) - of "xz": xz(op.cmdLineRest) - of "nsis": nsis(op.cmdLineRest) - of "geninstall": geninstall(op.cmdLineRest) - of "distrohelper": geninstall() - of "install": install(op.cmdLineRest) - of "testinstall": testUnixInstall() - of "test", "tests": tests(op.cmdLineRest) - of "temp": temp(op.cmdLineRest) - of "xtemp": xtemp(op.cmdLineRest) - of "winrelease": winRelease() - of "wintools": bundleWinTools() - of "nimble": buildNimble(existsDir(".git")) - of "nimsuggest": bundleNimsuggest(buildExe=true) - of "tools": buildTools(existsDir(".git")) - of "pushcsource", "pushcsources": pushCsources() - of "valgrind": valgrind(op.cmdLineRest) - else: showHelp() -of cmdEnd: showHelp() +when isMainModule: + var op = initOptParser() + op.next() + case op.kind + of cmdLongOption, cmdShortOption: showHelp() + of cmdArgument: + case normalize(op.key) + of "boot": boot(op.cmdLineRest) + of "clean": clean(op.cmdLineRest) + of "web": web(op.cmdLineRest) + of "doc", "docs": web("--onlyDocs " & op.cmdLineRest) + of "json2": web("--json2 " & op.cmdLineRest) + of "website": website(op.cmdLineRest & gaCode) + of "web0": + # undocumented command for Araq-the-merciful: + web(op.cmdLineRest & gaCode) + of "pdf": pdf() + of "csource", "csources": csource(op.cmdLineRest) + of "zip": zip(op.cmdLineRest) + of "xz": xz(op.cmdLineRest) + of "nsis": nsis(op.cmdLineRest) + of "geninstall": geninstall(op.cmdLineRest) + of "distrohelper": geninstall() + of "install": install(op.cmdLineRest) + of "testinstall": testUnixInstall() + of "test", "tests": tests(op.cmdLineRest) + of "temp": temp(op.cmdLineRest) + of "xtemp": xtemp(op.cmdLineRest) + #of "winrelease": winRelease() + of "wintools": bundleWinTools() + of "nimble": buildNimble(existsDir(".git")) + of "nimsuggest": bundleNimsuggest(buildExe=true) + of "tools": buildTools(existsDir(".git")) + of "pushcsource", "pushcsources": pushCsources() + of "valgrind": valgrind(op.cmdLineRest) + else: showHelp() + of cmdEnd: showHelp() diff --git a/lib/core/locks.nim b/lib/core/locks.nim index fbe9c8acf..f9add0037 100644 --- a/lib/core/locks.nim +++ b/lib/core/locks.nim @@ -19,6 +19,8 @@ type {.deprecated: [TLock: Lock, TCond: Cond].} +{.push stackTrace: off.} + proc initLock*(lock: var Lock) {.inline.} = ## Initializes the given lock. initSysLock(lock) @@ -59,9 +61,12 @@ proc signal*(cond: var Cond) {.inline.} = template withLock*(a: Lock, body: untyped) = ## Acquires the given lock, executes the statements in body and ## releases the lock after the statements finish executing. + mixin acquire, release a.acquire() {.locks: [a].}: try: body finally: a.release() + +{.pop.} diff --git a/lib/core/macros.nim b/lib/core/macros.nim index af1e9de28..3cfefb5c1 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -94,8 +94,9 @@ type ntyVarargs, ntyUnused, ntyError, - ntyBuiltinTypeClass, ntyConcept, ntyConceptInst, ntyComposite, - ntyAnd, ntyOr, ntyNot + ntyBuiltinTypeClass, ntyUserTypeClass, ntyUserTypeClassInst, + ntyCompositeTypeClass, ntyInferred, ntyAnd, ntyOr, ntyNot, + ntyAnything, ntyStatic, ntyFromExpr, ntyFieldAccessor, ntyVoid TNimTypeKinds* {.deprecated.} = set[NimTypeKind] NimSymKind* = enum @@ -149,6 +150,9 @@ proc `==`*(a, b: NimIdent): bool {.magic: "EqIdent", noSideEffect.} proc `==`*(a, b: NimNode): bool {.magic: "EqNimrodNode", noSideEffect.} ## compares two Nim nodes +proc `==`*(a, b: NimSym): bool {.magic: "EqNimrodNode", noSideEffect.} + ## compares two Nim symbols + proc sameType*(a, b: NimNode): bool {.magic: "SameNodeType", noSideEffect.} = ## compares two Nim nodes' types. Return true if the types are the same, ## eg. true when comparing alias with original type. @@ -317,10 +321,30 @@ proc toStrLit*(n: NimNode): NimNode {.compileTime.} = ## in a string literal node return newStrLitNode(repr(n)) -proc lineinfo*(n: NimNode): string {.magic: "NLineInfo", noSideEffect.} +type + LineInfo* = object + filename*: string + line*,column*: int + +proc `$`*(arg: Lineinfo): string = + result = arg.filename & "(" & $arg.line & ", " & $arg.column & ")" + +#proc lineinfo*(n: NimNode): LineInfo {.magic: "NLineInfo", noSideEffect.} ## returns the position the node appears in the original source file ## in the form filename(line, col) +proc getLine(arg: NimNode): int {.magic: "NLineInfo", noSideEffect.} +proc getColumn(arg: NimNode): int {.magic: "NLineInfo", noSideEffect.} +proc getFile(arg: NimNode): string {.magic: "NLineInfo", noSideEffect.} + +proc lineInfoObj*(n: NimNode): LineInfo {.compileTime.} = + result.filename = n.getFile + result.line = n.getLine + result.column = n.getColumn + +proc lineInfo*(arg: NimNode): string {.compileTime.} = + $arg.lineInfoObj + proc internalParseExpr(s: string): NimNode {. magic: "ParseExprToAst", noSideEffect.} @@ -527,10 +551,17 @@ proc newLit*[N,T](arg: array[N,T]): NimNode {.compileTime.} = result.add newLit(x) proc newLit*[T](arg: seq[T]): NimNode {.compileTime.} = - result = nnkBracket.newTree + var bracket = nnkBracket.newTree for x in arg: - result.add newLit(x) - result = nnkPrefix.newTree(bindSym"@", result) + bracket.add newLit(x) + + result = nnkCall.newTree( + nnkBracketExpr.newTree( + nnkAccQuoted.newTree( bindSym"@" ), + getTypeInst( bindSym"T" ) + ), + bracket + ) proc newLit*(arg: tuple): NimNode {.compileTime.} = result = nnkPar.newTree @@ -557,7 +588,8 @@ proc nestList*(theProc: NimIdent, proc treeRepr*(n: NimNode): string {.compileTime, benign.} = ## Convert the AST `n` to a human-readable tree-like string. ## - ## See also `repr` and `lispRepr`. + ## See also `repr`, `lispRepr`, and `astGenRepr`. + proc traverse(res: var string, level: int, n: NimNode) {.benign.} = for i in 0..level-1: res.add " " res.add(($n.kind).substr(3)) @@ -582,7 +614,7 @@ proc treeRepr*(n: NimNode): string {.compileTime, benign.} = proc lispRepr*(n: NimNode): string {.compileTime, benign.} = ## Convert the AST `n` to a human-readable lisp-like string, ## - ## See also `repr` and `treeRepr`. + ## See also `repr`, `treeRepr`, and `astGenRepr`. result = ($n.kind).substr(3) add(result, "(") @@ -605,9 +637,96 @@ proc lispRepr*(n: NimNode): string {.compileTime, benign.} = add(result, ")") +proc astGenRepr*(n: NimNode): string {.compileTime, benign.} = + ## Convert the AST `n` to the code required to generate that AST. So for example + ## + ## .. code-block:: nim + ## astGenRepr: + ## echo "Hello world" + ## + ## Would output: + ## + ## .. code-block:: nim + ## nnkStmtList.newTree( + ## nnkCommand.newTree( + ## newIdentNode(!"echo"), + ## newLit("Hello world") + ## ) + ## ) + ## + ## See also `repr`, `treeRepr`, and `lispRepr`. + + const + NodeKinds = {nnkEmpty, nnkNilLit, nnkIdent, nnkSym, nnkNone} + LitKinds = {nnkCharLit..nnkInt64Lit, nnkFloatLit..nnkFloat64Lit, nnkStrLit..nnkTripleStrLit} + + proc escape(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect.} = + ## Functions copied from strutils + proc toHex(x: BiggestInt, len: Positive): string {.noSideEffect, rtl.} = + const + HexChars = "0123456789ABCDEF" + var + t = x + result = newString(len) + for j in countdown(len-1, 0): + result[j] = HexChars[t and 0xF] + t = t shr 4 + # handle negative overflow + if t == 0 and x < 0: t = -1 + + result = newStringOfCap(s.len + s.len shr 2) + result.add(prefix) + for c in items(s): + case c + of '\0'..'\31', '\128'..'\255': + add(result, "\\x") + add(result, toHex(ord(c), 2)) + of '\\': add(result, "\\\\") + of '\'': add(result, "\\'") + of '\"': add(result, "\\\"") + else: add(result, c) + add(result, suffix) + + proc traverse(res: var string, level: int, n: NimNode) {.benign.} = + for i in 0..level-1: res.add " " + if n.kind in NodeKinds: + res.add("new" & ($n.kind).substr(3) & "Node(") + elif n.kind in LitKinds: + res.add("newLit(") + else: + res.add($n.kind) + + case n.kind + of nnkEmpty: discard + of nnkNilLit: res.add("nil") + of nnkCharLit: res.add("'" & $chr(n.intVal) & "'") + of nnkIntLit..nnkInt64Lit: res.add($n.intVal) + of nnkFloatLit..nnkFloat64Lit: res.add($n.floatVal) + of nnkStrLit..nnkTripleStrLit: res.add($n.strVal.escape()) + of nnkIdent: res.add("!" & ($n.ident).escape()) + of nnkSym: res.add(($n.symbol).escape()) + of nnkNone: assert false + else: + res.add(".newTree(") + for j in 0..<n.len: + res.add "\n" + traverse(res, level + 1, n[j]) + if j != n.len-1: + res.add(",") + + res.add("\n") + for i in 0..level-1: res.add " " + res.add(")") + + if n.kind in NodeKinds+LitKinds: + res.add(")") + + result = "" + traverse(result, 0, n) + macro dumpTree*(s: untyped): untyped = echo s.treeRepr ## Accepts a block of nim code and prints the parsed abstract syntax - ## tree using the `toTree` function. Printing is done *at compile time*. + ## tree using the `treeRepr` function. Printing is done *at compile time*. ## ## You can use this as a tool to explore the Nim's abstract syntax ## tree and to discover what kind of nodes must be created to represent @@ -615,7 +734,16 @@ macro dumpTree*(s: untyped): untyped = echo s.treeRepr macro dumpLisp*(s: untyped): untyped = echo s.lispRepr ## Accepts a block of nim code and prints the parsed abstract syntax - ## tree using the `toLisp` function. Printing is done *at compile time*. + ## tree using the `lispRepr` function. Printing is done *at compile time*. + ## + ## See `dumpTree`. + +macro dumpAstGen*(s: untyped): untyped = echo s.astGenRepr + ## Accepts a block of nim code and prints the parsed abstract syntax + ## tree using the `astGenRepr` function. Printing is done *at compile time*. + ## + ## You can use this as a tool to write macros quicker by writing example + ## outputs and then copying the snippets into the macro for modification. ## ## See `dumpTree`. @@ -766,6 +894,8 @@ template expectRoutine(node: NimNode) = proc name*(someProc: NimNode): NimNode {.compileTime.} = someProc.expectRoutine result = someProc[0] + if result.kind == nnkPostfix: + result = result[1] proc `name=`*(someProc: NimNode; val: NimNode) {.compileTime.} = someProc.expectRoutine someProc[0] = val diff --git a/lib/core/typeinfo.nim b/lib/core/typeinfo.nim index c4b2ceca3..72b139202 100644 --- a/lib/core/typeinfo.nim +++ b/lib/core/typeinfo.nim @@ -97,7 +97,7 @@ proc newObj(typ: PNimType, size: int): pointer {.importCompilerProc.} proc newSeq(typ: PNimType, len: int): pointer {.importCompilerProc.} proc objectInit(dest: pointer, typ: PNimType) {.importCompilerProc.} -template `+!!`(a, b: expr): expr = cast[pointer](cast[ByteAddress](a) + b) +template `+!!`(a, b): untyped = cast[pointer](cast[ByteAddress](a) + b) proc getDiscriminant(aa: pointer, n: ptr TNimNode): int = assert(n.kind == nkCase) diff --git a/lib/deprecated/pure/sockets.nim b/lib/deprecated/pure/sockets.nim index 415078126..153db9ed8 100644 --- a/lib/deprecated/pure/sockets.nim +++ b/lib/deprecated/pure/sockets.nim @@ -42,6 +42,8 @@ from times import epochTime when defined(ssl): import openssl +else: + type SSLAcceptResult = int when defined(Windows): import winlean @@ -206,16 +208,16 @@ proc htons*(x: int16): int16 = ## order, this is a no-op; otherwise, it performs a 2-byte swap operation. result = sockets.ntohs(x) -template ntohl(x: uint32): expr = +template ntohl(x: uint32): uint32 = cast[uint32](sockets.ntohl(cast[int32](x))) -template ntohs(x: uint16): expr = +template ntohs(x: uint16): uint16 = cast[uint16](sockets.ntohs(cast[int16](x))) -template htonl(x: uint32): expr = +template htonl(x: uint32): uint32 = sockets.ntohl(x) -template htons(x: uint16): expr = +template htons(x: uint16): uint16 = sockets.ntohs(x) when defined(Posix): @@ -442,14 +444,13 @@ proc parseIp4*(s: string): BiggestInt = if s[i] != '\0': invalidIp4(s) result = BiggestInt(a shl 24 or b shl 16 or c shl 8 or d) -template gaiNim(a, p, h, list: expr): stmt = - block: - var gaiResult = getaddrinfo(a, $p, addr(h), list) - if gaiResult != 0'i32: - when defined(windows): - raiseOSError(osLastError()) - else: - raiseOSError(osLastError(), $gai_strerror(gaiResult)) +template gaiNim(a, p, h, list: untyped): untyped = + var gaiResult = getaddrinfo(a, $p, addr(h), list) + if gaiResult != 0'i32: + when defined(windows): + raiseOSError(osLastError()) + else: + raiseOSError(osLastError(), $gai_strerror(gaiResult)) proc bindAddr*(socket: Socket, port = Port(0), address = "") {. tags: [ReadIOEffect].} = @@ -493,8 +494,8 @@ proc getSockName*(socket: Socket): Port = raiseOSError(osLastError()) result = Port(sockets.ntohs(name.sin_port)) -template acceptAddrPlain(noClientRet, successRet: expr, - sslImplementation: stmt): stmt {.immediate.} = +template acceptAddrPlain(noClientRet, successRet: SSLAcceptResult or int, + sslImplementation: untyped): untyped = assert(client != nil) var sockAddress: Sockaddr_in var addrLen = sizeof(sockAddress).SockLen @@ -550,7 +551,7 @@ proc acceptAddr*(server: Socket, client: var Socket, address: var string) {. ## ## **Warning:** When using SSL with non-blocking sockets, it is best to use ## the acceptAddrSSL procedure as this procedure will most likely block. - acceptAddrPlain(-1, -1): + acceptAddrPlain(SSLAcceptResult(-1), SSLAcceptResult(-1)): when defined(ssl): if server.isSSL: # We must wrap the client sock in a ssl context. @@ -594,7 +595,7 @@ when defined(ssl): ## ## ``AcceptNoClient`` will be returned when no client is currently attempting ## to connect. - template doHandshake(): stmt = + template doHandshake(): untyped = when defined(ssl): if server.isSSL: client.setBlocking(false) @@ -1278,7 +1279,7 @@ proc recvLine*(socket: Socket, line: var TaintedString, timeout = -1): bool {. ## **Deprecated since version 0.9.2**: This function has been deprecated in ## favour of readLine. - template addNLIfEmpty(): stmt = + template addNLIfEmpty(): untyped = if line.len == 0: line.add("\c\L") @@ -1319,7 +1320,7 @@ proc readLine*(socket: Socket, line: var TaintedString, timeout = -1) {. ## A timeout can be specified in milliseconds, if data is not received within ## the specified time an ETimeout exception will be raised. - template addNLIfEmpty(): stmt = + template addNLIfEmpty(): untyped = if line.len == 0: line.add("\c\L") diff --git a/lib/impure/nre/private/util.nim b/lib/impure/nre/private/util.nim index 253bfada7..12d2506ea 100644 --- a/lib/impure/nre/private/util.nim +++ b/lib/impure/nre/private/util.nim @@ -16,7 +16,7 @@ proc checkNil(arg: string): string = else: return arg -template formatStr*(howExpr, namegetter, idgetter: expr): expr = +template formatStr*(howExpr, namegetter, idgetter): untyped = let how = howExpr var val = newStringOfCap(how.len) var i = 0 diff --git a/lib/js/jsffi.nim b/lib/js/jsffi.nim index 3b3f38e41..0d359d9e6 100644 --- a/lib/js/jsffi.nim +++ b/lib/js/jsffi.nim @@ -408,20 +408,19 @@ macro `{}`*(typ: typedesc, xs: varargs[untyped]): auto = kString = quote do: when compiles($`k`): $`k` else: "invalid" v = x[1] - body.add(quote do: + body.add quote do: when compiles(`a`.`k`): `a`.`k` = `v` elif compiles(`a`[`k`]): `a`[`k`] = `v` else: `a`[`kString`] = `v` - ) + else: error("Expression `" & $x.toStrLit & "` not allowed in `{}` macro") - body.add(quote do: + body.add quote do: return `a` - ) result = quote do: proc inner(): `typ` {.gensym.} = diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index ce63d780c..13016bfc0 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -765,7 +765,7 @@ proc renderTocEntries*(d: var RstGenerator, j: var int, lvl: int, result.add(tmp) proc renderImage(d: PDoc, n: PRstNode, result: var string) = - template valid(s): expr = + template valid(s): bool = s.len > 0 and allCharsInSet(s, {'.','/',':','%','_','\\','\128'..'\xFF'} + Digits + Letters + WhiteSpace) let @@ -1194,7 +1194,7 @@ proc defaultConfig*(): StringTableRef = ## ``rstToHtml`` to generate the bare minimum HTML. result = newStringTable(modeStyleInsensitive) - template setConfigVar(key, val: expr) = + template setConfigVar(key, val) = result[key] = val # If you need to modify these values, it might be worth updating the template diff --git a/lib/posix/posix.nim b/lib/posix/posix.nim index 55d1dd2eb..b635c0b0b 100644 --- a/lib/posix/posix.nim +++ b/lib/posix/posix.nim @@ -92,7 +92,7 @@ else: # There used to be this name in posix.nim a long time ago, not sure why! {.deprecated: [cSIG_HOLD: SIG_HOLD].} -when not defined(macosx): +when not defined(macosx) and not defined(android): proc st_atime*(s: Stat): Time {.inline.} = ## Second-granularity time of last access result = s.st_atim.tv_sec diff --git a/lib/posix/posix_other.nim b/lib/posix/posix_other.nim index b1cc244b7..69f6acfb8 100644 --- a/lib/posix/posix_other.nim +++ b/lib/posix/posix_other.nim @@ -15,7 +15,7 @@ const hasSpawnH = not defined(haiku) # should exist for every Posix system nowadays hasAioH = defined(linux) -when defined(linux): +when defined(linux) and not defined(android): # On Linux: # timer_{create,delete,settime,gettime}, # clock_{getcpuclockid, getres, gettime, nanosleep, settime} lives in librt @@ -46,9 +46,9 @@ type d_ino*: Ino ## File serial number. when defined(dragonfly): # DragonflyBSD doesn't have `d_reclen` field. - d_type*: uint8 + d_type*: uint8 elif defined(linux) or defined(macosx) or defined(freebsd) or - defined(netbsd) or defined(openbsd): + defined(netbsd) or defined(openbsd) or defined(genode): d_reclen*: cshort ## Length of this record. (not POSIX) d_type*: int8 ## Type of file; not supported by all filesystem types. ## (not POSIX) @@ -215,7 +215,7 @@ type ## For a typed memory object, the length in bytes. ## For other file types, the use of this field is ## unspecified. - when defined(macosx): + when defined(macosx) or defined(android): st_atime*: Time ## Time of last access. st_mtime*: Time ## Time of last data modification. st_ctime*: Time ## Time of last status change. @@ -572,7 +572,8 @@ else: MAP_POPULATE*: cint = 0 when defined(linux) or defined(nimdoc): - when defined(alpha) or defined(mips) or defined(parisc) or + when defined(alpha) or defined(mips) or defined(mipsel) or + defined(mips64) or defined(mips64el) or defined(parisc) or defined(sparc) or defined(nimdoc): const SO_REUSEPORT* = cint(0x0200) ## Multiple binding: load balancing on incoming TCP connections diff --git a/lib/posix/termios.nim b/lib/posix/termios.nim index 21b21aefb..f86e408fb 100644 --- a/lib/posix/termios.nim +++ b/lib/posix/termios.nim @@ -174,7 +174,7 @@ var # Compare a character C to a value VAL from the `cc' array in a # `struct termios'. If VAL is _POSIX_VDISABLE, no character can match it. -template cceq*(val, c: expr): expr = +template cceq*(val, c): untyped = c == val and val != POSIX_VDISABLE # Return the output baud rate stored in *TERMIOS_P. diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim index 8c1cf6b18..281d5b848 100644 --- a/lib/pure/asyncdispatch.nim +++ b/lib/pure/asyncdispatch.nim @@ -9,11 +9,12 @@ include "system/inclrtl" -import os, tables, strutils, times, heapqueue, options - +import os, tables, strutils, times, heapqueue, options, asyncstreams +import asyncfutures except callSoon import nativesockets, net, deques export Port, SocketFlag +export asyncfutures, asyncstreams #{.injectStmt: newGcInvariant().} @@ -159,8 +160,6 @@ export Port, SocketFlag # TODO: Check if yielded future is nil and throw a more meaningful exception -include includes/asyncfutures - type PDispatcherBase = ref object of RootRef timers*: HeapQueue[tuple[finishAt: float, fut: Future[void]]] @@ -190,6 +189,12 @@ proc adjustedTimeout(p: PDispatcherBase, timeout: int): int {.inline.} = result = int((timerTimeout - curTime) * 1000) if result < 0: result = 0 +proc callSoon(cbproc: proc ()) {.gcsafe.} + +proc initCallSoonProc = + if asyncfutures.getCallSoonProc().isNil: + asyncfutures.setCallSoonProc(callSoon) + when defined(windows) or defined(nimdoc): import winlean, sets, hashes type @@ -237,15 +242,17 @@ when defined(windows) or defined(nimdoc): result.callbacks = initDeque[proc ()](64) var gDisp{.threadvar.}: PDispatcher ## Global dispatcher - proc getGlobalDispatcher*(): PDispatcher = - ## Retrieves the global thread-local dispatcher. - if gDisp.isNil: gDisp = newDispatcher() - result = gDisp proc setGlobalDispatcher*(disp: PDispatcher) = if not gDisp.isNil: assert gDisp.callbacks.len == 0 gDisp = disp + initCallSoonProc() + + proc getGlobalDispatcher*(): PDispatcher = + if gDisp.isNil: + setGlobalDispatcher(newDispatcher()) + result = gDisp proc register*(fd: AsyncFD) = ## Registers ``fd`` with the dispatcher. @@ -932,14 +939,17 @@ else: result.callbacks = initDeque[proc ()](64) var gDisp{.threadvar.}: PDispatcher ## Global dispatcher - proc getGlobalDispatcher*(): PDispatcher = - if gDisp.isNil: gDisp = newDispatcher() - result = gDisp proc setGlobalDispatcher*(disp: PDispatcher) = if not gDisp.isNil: assert gDisp.callbacks.len == 0 gDisp = disp + initCallSoonProc() + + proc getGlobalDispatcher*(): PDispatcher = + if gDisp.isNil: + setGlobalDispatcher(newDispatcher()) + result = gDisp proc update(fd: AsyncFD, events: set[Event]) = let p = getGlobalDispatcher() @@ -1307,7 +1317,7 @@ proc recvLine*(socket: AsyncFD): Future[string] {.async, deprecated.} = ## ## **Deprecated since version 0.15.0**: Use ``asyncnet.recvLine()`` instead. - template addNLIfEmpty(): stmt = + template addNLIfEmpty(): untyped = if result.len == 0: result.add("\c\L") @@ -1327,7 +1337,7 @@ proc recvLine*(socket: AsyncFD): Future[string] {.async, deprecated.} = return add(result, c) -proc callSoon*(cbproc: proc ()) = +proc callSoon(cbproc: proc ()) = ## Schedule `cbproc` to be called as soon as possible. ## The callback is called when control returns to the event loop. getGlobalDispatcher().callbacks.addLast(cbproc) diff --git a/lib/pure/asyncfile.nim b/lib/pure/asyncfile.nim index 8fb30075c..9f4da16a3 100644 --- a/lib/pure/asyncfile.nim +++ b/lib/pure/asyncfile.nim @@ -81,27 +81,32 @@ proc getFileSize*(f: AsyncFile): int64 = else: result = lseek(f.fd.cint, 0, SEEK_END) +proc newAsyncFile*(fd: AsyncFd): AsyncFile = + ## Creates `AsyncFile` with a previously opened file descriptor `fd`. + new result + result.fd = fd + register(result.fd) + proc openAsync*(filename: string, mode = fmRead): AsyncFile = ## Opens a file specified by the path in ``filename`` using ## the specified ``mode`` asynchronously. - new result when defined(windows) or defined(nimdoc): let flags = FILE_FLAG_OVERLAPPED or FILE_ATTRIBUTE_NORMAL let desiredAccess = getDesiredAccess(mode) let creationDisposition = getCreationDisposition(mode, filename) when useWinUnicode: - result.fd = createFileW(newWideCString(filename), desiredAccess, + let fd = createFileW(newWideCString(filename), desiredAccess, FILE_SHARE_READ, nil, creationDisposition, flags, 0).AsyncFd else: - result.fd = createFileA(filename, desiredAccess, + let fd = createFileA(filename, desiredAccess, FILE_SHARE_READ, nil, creationDisposition, flags, 0).AsyncFd - if result.fd.Handle == INVALID_HANDLE_VALUE: + if fd.Handle == INVALID_HANDLE_VALUE: raiseOSError(osLastError()) - register(result.fd) + result = newAsyncFile(fd) if mode == fmAppend: result.offset = getFileSize(result) @@ -110,11 +115,11 @@ proc openAsync*(filename: string, mode = fmRead): AsyncFile = let flags = getPosixFlags(mode) # RW (Owner), RW (Group), R (Other) let perm = S_IRUSR or S_IWUSR or S_IRGRP or S_IWGRP or S_IROTH - result.fd = open(filename, flags, perm).AsyncFD - if result.fd.cint == -1: + let fd = open(filename, flags, perm).AsyncFD + if fd.cint == -1: raiseOSError(osLastError()) - register(result.fd) + result = newAsyncFile(fd) proc readBuffer*(f: AsyncFile, buf: pointer, size: int): Future[int] = ## Read ``size`` bytes from the specified file asynchronously starting at diff --git a/lib/pure/includes/asyncfutures.nim b/lib/pure/asyncfutures.nim index 6af5bf3cf..bebd19611 100644 --- a/lib/pure/includes/asyncfutures.nim +++ b/lib/pure/asyncfutures.nim @@ -1,8 +1,16 @@ +import os, tables, strutils, times, heapqueue, options, deques # TODO: This shouldn't need to be included, but should ideally be exported. type + CallbackFunc = proc () {.closure, gcsafe.} + + CallbackList = object + function: CallbackFunc + next: ref CallbackList + FutureBase* = ref object of RootObj ## Untyped future. - cb: proc () {.closure,gcsafe.} + callbacks: CallbackList + finished: bool error*: ref Exception ## Stored exception errorStackTrace*: string @@ -16,12 +24,6 @@ type FutureVar*[T] = distinct Future[T] - FutureStream*[T] = ref object of FutureBase ## Special future that acts as - ## a queue. Its API is still - ## experimental and so is - ## subject to change. - queue: Deque[T] - FutureError* = object of Exception cause*: FutureBase @@ -30,7 +32,27 @@ type when not defined(release): var currentID = 0 -proc callSoon*(cbproc: proc ()) {.gcsafe.} +var callSoonProc {.threadvar.}: proc (cbproc: proc ()) {.gcsafe.} + +proc getCallSoonProc*(): (proc(cbproc: proc ()) {.gcsafe.}) = + ## Get current implementation of ``callSoon``. + return callSoonProc + +proc setCallSoonProc*(p: (proc(cbproc: proc ()) {.gcsafe.})) = + ## Change current implementation of ``callSoon``. This is normally called when dispatcher from ``asyncdispatcher`` is initialized. + callSoonProc = p + +proc callSoon*(cbproc: proc ()) = + ## Call ``cbproc`` "soon". + ## + ## If async dispatcher is running, ``cbproc`` will be executed during next dispatcher tick. + ## + ## If async dispatcher is not running, ``cbproc`` will be executed immediately. + if callSoonProc.isNil: + # Loop not initialized yet. Call the function directly to allow setup code to use futures. + cbproc() + else: + callSoonProc(cbproc) template setupFutureBase(fromProc: string) = new(result) @@ -56,22 +78,6 @@ proc newFutureVar*[T](fromProc = "unspecified"): FutureVar[T] = ## that this future belongs to, is a good habit as it helps with debugging. result = FutureVar[T](newFuture[T](fromProc)) -proc newFutureStream*[T](fromProc = "unspecified"): FutureStream[T] = - ## Create a new ``FutureStream``. This future's callback is activated when - ## two events occur: - ## - ## * New data is written into the future stream. - ## * The future stream is completed (this means that no more data will be - ## written). - ## - ## Specifying ``fromProc``, which is a string specifying the name of the proc - ## that this future belongs to, is a good habit as it helps with debugging. - ## - ## **Note:** The API of FutureStream is still new and so has a higher - ## likelihood of changing in the future. - setupFutureBase(fromProc) - result.queue = initDeque[T]() - proc clean*[T](future: FutureVar[T]) = ## Resets the ``finished`` status of ``future``. Future[T](future).finished = false @@ -98,6 +104,33 @@ proc checkFinished[T](future: Future[T]) = err.cause = future raise err +proc call(callbacks: var CallbackList) = + var current = callbacks + + while true: + if not current.function.isNil: + callSoon(current.function) + + if current.next.isNil: + break + else: + current = current.next[] + + # callback will be called only once, let GC collect them now + callbacks.next = nil + callbacks.function = nil + +proc add(callbacks: var CallbackList, function: CallbackFunc) = + if callbacks.function.isNil: + callbacks.function = function + assert callbacks.next == nil + else: + let newNext = new(ref CallbackList) + newNext.function = callbacks.function + newNext.next = callbacks.next + callbacks.next = newNext + callbacks.function = function + proc complete*[T](future: Future[T], val: T) = ## Completes ``future`` with value ``val``. #assert(not future.finished, "Future already finished, cannot finish twice.") @@ -105,8 +138,7 @@ proc complete*[T](future: Future[T], val: T) = assert(future.error == nil) future.value = val future.finished = true - if future.cb != nil: - future.cb() + future.callbacks.call() proc complete*(future: Future[void]) = ## Completes a void ``future``. @@ -114,8 +146,7 @@ proc complete*(future: Future[void]) = checkFinished(future) assert(future.error == nil) future.finished = true - if future.cb != nil: - future.cb() + future.callbacks.call() proc complete*[T](future: FutureVar[T]) = ## Completes a ``FutureVar``. @@ -123,8 +154,7 @@ proc complete*[T](future: FutureVar[T]) = checkFinished(fut) assert(fut.error == nil) fut.finished = true - if fut.cb != nil: - fut.cb() + fut.callbacks.call() proc complete*[T](future: FutureVar[T], val: T) = ## Completes a ``FutureVar`` with value ``val``. @@ -135,14 +165,7 @@ proc complete*[T](future: FutureVar[T], val: T) = assert(fut.error.isNil()) fut.finished = true fut.value = val - if not fut.cb.isNil(): - fut.cb() - -proc complete*[T](future: FutureStream[T]) = - ## Completes a ``FutureStream`` signalling the end of data. - future.finished = true - if not future.cb.isNil(): - future.cb() + fut.callbacks.call() proc fail*[T](future: Future[T], error: ref Exception) = ## Completes ``future`` with ``error``. @@ -152,26 +175,40 @@ proc fail*[T](future: Future[T], error: ref Exception) = future.error = error future.errorStackTrace = if getStackTrace(error) == "": getStackTrace() else: getStackTrace(error) - if future.cb != nil: - future.cb() + future.callbacks.call() + +proc clearCallbacks(future: FutureBase) = + future.callbacks.function = nil + future.callbacks.next = nil + +proc addCallback*(future: FutureBase, cb: proc() {.closure,gcsafe.}) = + ## Adds the callbacks proc to be called when the future completes. + ## + ## If future has already completed then ``cb`` will be called immediately. + assert cb != nil + if future.finished: + callSoon(cb) else: - # This is to prevent exceptions from being silently ignored when a future - # is discarded. - # TODO: This may turn out to be a bad idea. - # Turns out this is a bad idea. - #raise error - discard + future.callbacks.add cb + +proc addCallback*[T](future: Future[T], + cb: proc (future: Future[T]) {.closure,gcsafe.}) = + ## Adds the callbacks proc to be called when the future completes. + ## + ## If future has already completed then ``cb`` will be called immediately. + future.addCallback( + proc() = + cb(future) + ) proc `callback=`*(future: FutureBase, cb: proc () {.closure,gcsafe.}) = - ## Sets the callback proc to be called when the future completes. + ## Clears the list of callbacks and sets the callback proc to be called when the future completes. ## ## If future has already completed then ``cb`` will be called immediately. ## - ## **Note**: You most likely want the other ``callback`` setter which - ## passes ``future`` as a param to the callback. - future.cb = cb - if future.finished: - callSoon(future.cb) + ## It's recommended to use ``addCallback`` or ``then`` instead. + future.clearCallbacks + future.addCallback cb proc `callback=`*[T](future: Future[T], cb: proc (future: Future[T]) {.closure,gcsafe.}) = @@ -180,20 +217,6 @@ proc `callback=`*[T](future: Future[T], ## If future has already completed then ``cb`` will be called immediately. future.callback = proc () = cb(future) -proc `callback=`*[T](future: FutureStream[T], - cb: proc (future: FutureStream[T]) {.closure,gcsafe.}) = - ## Sets the callback proc to be called when data was placed inside the - ## future stream. - ## - ## The callback is also called when the future is completed. So you should - ## use ``finished`` to check whether data is available. - ## - ## If the future stream already has data or is finished then ``cb`` will be - ## called immediately. - future.cb = proc () = cb(future) - if future.queue.len > 0 or future.finished: - callSoon(future.cb) - proc injectStacktrace[T](future: Future[T]) = # TODO: Come up with something better. when not defined(release): @@ -240,18 +263,12 @@ proc mget*[T](future: FutureVar[T]): var T = ## Future has not been finished. result = Future[T](future).value -proc finished*[T](future: Future[T] | FutureVar[T] | FutureStream[T]): bool = +proc finished*[T](future: Future[T] | FutureVar[T]): bool = ## Determines whether ``future`` has completed. ## ## ``True`` may indicate an error or a value. Use ``failed`` to distinguish. - ## - ## For a ``FutureStream`` a ``true`` value means that no more data will be - ## placed inside the stream _and_ that there is no data waiting to be - ## retrieved. when future is FutureVar[T]: result = (Future[T](future)).finished - elif future is FutureStream[T]: - result = future.finished and future.queue.len == 0 else: result = future.finished @@ -259,57 +276,6 @@ proc failed*(future: FutureBase): bool = ## Determines whether ``future`` completed with an error. return future.error != nil -proc write*[T](future: FutureStream[T], value: T): Future[void] = - ## Writes the specified value inside the specified future stream. - ## - ## This will raise ``ValueError`` if ``future`` is finished. - result = newFuture[void]("FutureStream.put") - if future.finished: - let msg = "FutureStream is finished and so no longer accepts new data." - result.fail(newException(ValueError, msg)) - return - # TODO: Implement limiting of the streams storage to prevent it growing - # infinitely when no reads are occuring. - future.queue.addLast(value) - if not future.cb.isNil: future.cb() - result.complete() - -proc read*[T](future: FutureStream[T]): Future[(bool, T)] = - ## Returns a future that will complete when the ``FutureStream`` has data - ## placed into it. The future will be completed with the oldest - ## value stored inside the stream. The return value will also determine - ## whether data was retrieved, ``false`` means that the future stream was - ## completed and no data was retrieved. - ## - ## This function will remove the data that was returned from the underlying - ## ``FutureStream``. - var resFut = newFuture[(bool, T)]("FutureStream.take") - let savedCb = future.cb - future.callback = - proc (fs: FutureStream[T]) = - # We don't want this callback called again. - future.cb = nil - - # The return value depends on whether the FutureStream has finished. - var res: (bool, T) - if finished(fs): - # Remember, this callback is called when the FutureStream is completed. - res[0] = false - else: - res[0] = true - res[1] = fs.queue.popFirst() - - if not resFut.finished: - resFut.complete(res) - - # If the saved callback isn't nil then let's call it. - if not savedCb.isNil: savedCb() - return resFut - -proc len*[T](future: FutureStream[T]): int = - ## Returns the amount of data pieces inside the stream. - future.queue.len - proc asyncCheck*[T](future: Future[T]) = ## Sets a callback on ``future`` which raises an exception if the future ## finished with an error. diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim index a374e80e8..b7b57a82f 100644 --- a/lib/pure/asynchttpserver.nim +++ b/lib/pure/asynchttpserver.nim @@ -127,7 +127,7 @@ proc parseProtocol(protocol: string): tuple[orig: string, major, minor: int] = i.inc protocol.parseInt(result.minor, i) proc sendStatus(client: AsyncSocket, status: string): Future[void] = - client.send("HTTP/1.1 " & status & "\c\L\c\L") + client.send("HTTP/1.1 " & status & "\c\L\c\L") proc processClient(client: AsyncSocket, address: string, callback: proc (request: Request): @@ -233,7 +233,7 @@ proc processClient(client: AsyncSocket, address: string, await request.respond(Http400, "Bad Request. Content-Length does not match actual.") continue elif request.reqMethod == HttpPost: - await request.respond(Http400, "Bad Request. No Content-Length.") + await request.respond(Http411, "Content-Length required.") continue # Call the user's callback. diff --git a/lib/pure/asyncmacro.nim b/lib/pure/asyncmacro.nim index 89b216b25..6e7d7993f 100644 --- a/lib/pure/asyncmacro.nim +++ b/lib/pure/asyncmacro.nim @@ -28,7 +28,7 @@ template createCb(retFutureSym, iteratorNameSym, name, futureVarCompletions: untyped) = var nameIterVar = iteratorNameSym #{.push stackTrace: off.} - proc cb {.closure,gcsafe.} = + proc cb0 {.closure.} = try: if not nameIterVar.finished: var next = nameIterVar() @@ -38,7 +38,10 @@ template createCb(retFutureSym, iteratorNameSym, "`nil` Future?" raise newException(AssertionError, msg % name) else: - next.callback = cb + {.gcsafe.}: + {.push hint[ConvFromXtoItselfNotNeeded]: off.} + next.callback = (proc() {.closure, gcsafe.})(cb0) + {.pop.} except: futureVarCompletions @@ -49,7 +52,7 @@ template createCb(retFutureSym, iteratorNameSym, else: retFutureSym.fail(getCurrentException()) - cb() + cb0() #{.pop.} proc generateExceptionCheck(futSym, tryStmt, rootReceiver, fromNode: NimNode): NimNode {.compileTime.} = @@ -379,7 +382,10 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} = procBody, nnkIteratorDef) closureIterator.pragma = newNimNode(nnkPragma, lineInfoFrom=prc.body) closureIterator.addPragma(newIdentNode("closure")) - closureIterator.addPragma(newIdentNode("gcsafe")) + + # If proc has an explicit gcsafe pragma, we add it to iterator as well. + if prc.pragma.findChild(it.kind in {nnkSym, nnkIdent} and $it == "gcsafe") != nil: + closureIterator.addPragma(newIdentNode("gcsafe")) outerProcBody.add(closureIterator) # -> createCb(retFuture) diff --git a/lib/pure/asyncnet.nim b/lib/pure/asyncnet.nim index 5de65efe0..5be457d2a 100644 --- a/lib/pure/asyncnet.nim +++ b/lib/pure/asyncnet.nim @@ -220,7 +220,7 @@ when defineSsl: raiseSSLError("Cannot appease SSL.") template sslLoop(socket: AsyncSocket, flags: set[SocketFlag], - op: expr) = + op: untyped) = var opResult {.inject.} = -1.cint while opResult < 0: # Call the desired operation. @@ -490,7 +490,7 @@ proc recvLineInto*(socket: AsyncSocket, resString: FutureVar[string], # them when the result future is completed. # Can we replace the result future with the FutureVar? - template addNLIfEmpty(): stmt = + template addNLIfEmpty(): untyped = if resString.mget.len == 0: resString.mget.add("\c\L") diff --git a/lib/pure/asyncstreams.nim b/lib/pure/asyncstreams.nim new file mode 100644 index 000000000..d3ea143f3 --- /dev/null +++ b/lib/pure/asyncstreams.nim @@ -0,0 +1,105 @@ +import asyncfutures + +import deques + +type + FutureStream*[T] = ref object ## Special future that acts as + ## a queue. Its API is still + ## experimental and so is + ## subject to change. + queue: Deque[T] + finished: bool + cb: proc () {.closure, gcsafe.} + +proc newFutureStream*[T](fromProc = "unspecified"): FutureStream[T] = + ## Create a new ``FutureStream``. This future's callback is activated when + ## two events occur: + ## + ## * New data is written into the future stream. + ## * The future stream is completed (this means that no more data will be + ## written). + ## + ## Specifying ``fromProc``, which is a string specifying the name of the proc + ## that this future belongs to, is a good habit as it helps with debugging. + ## + ## **Note:** The API of FutureStream is still new and so has a higher + ## likelihood of changing in the future. + result = FutureStream[T](finished: false, cb: nil) + result.queue = initDeque[T]() + +proc complete*[T](future: FutureStream[T]) = + ## Completes a ``FutureStream`` signalling the end of data. + future.finished = true + if not future.cb.isNil: + future.cb() + +proc `callback=`*[T](future: FutureStream[T], + cb: proc (future: FutureStream[T]) {.closure,gcsafe.}) = + ## Sets the callback proc to be called when data was placed inside the + ## future stream. + ## + ## The callback is also called when the future is completed. So you should + ## use ``finished`` to check whether data is available. + ## + ## If the future stream already has data or is finished then ``cb`` will be + ## called immediately. + future.cb = proc () = cb(future) + if future.queue.len > 0 or future.finished: + callSoon(future.cb) + +proc finished*[T](future: FutureStream[T]): bool = + ## Check if a ``FutureStream`` is finished. ``true`` value means that + ## no more data will be placed inside the stream _and_ that there is + ## no data waiting to be retrieved. + result = future.finished and future.queue.len == 0 + +proc write*[T](future: FutureStream[T], value: T): Future[void] = + ## Writes the specified value inside the specified future stream. + ## + ## This will raise ``ValueError`` if ``future`` is finished. + result = newFuture[void]("FutureStream.put") + if future.finished: + let msg = "FutureStream is finished and so no longer accepts new data." + result.fail(newException(ValueError, msg)) + return + # TODO: Implement limiting of the streams storage to prevent it growing + # infinitely when no reads are occuring. + future.queue.addLast(value) + if not future.cb.isNil: future.cb() + result.complete() + +proc read*[T](future: FutureStream[T]): Future[(bool, T)] = + ## Returns a future that will complete when the ``FutureStream`` has data + ## placed into it. The future will be completed with the oldest + ## value stored inside the stream. The return value will also determine + ## whether data was retrieved, ``false`` means that the future stream was + ## completed and no data was retrieved. + ## + ## This function will remove the data that was returned from the underlying + ## ``FutureStream``. + var resFut = newFuture[(bool, T)]("FutureStream.take") + let savedCb = future.cb + future.callback = + proc (fs: FutureStream[T]) = + # We don't want this callback called again. + future.cb = nil + + # The return value depends on whether the FutureStream has finished. + var res: (bool, T) + if finished(fs): + # Remember, this callback is called when the FutureStream is completed. + res[0] = false + else: + res[0] = true + res[1] = fs.queue.popFirst() + + if not resFut.finished: + resFut.complete(res) + + # If the saved callback isn't nil then let's call it. + if not savedCb.isNil: savedCb() + return resFut + +proc len*[T](future: FutureStream[T]): int = + ## Returns the amount of data pieces inside the stream. + future.queue.len diff --git a/lib/pure/basic2d.nim b/lib/pure/basic2d.nim index e4696c6a8..31b3814d6 100644 --- a/lib/pure/basic2d.nim +++ b/lib/pure/basic2d.nim @@ -116,13 +116,13 @@ proc safeArccos(v:float):float= return arccos(clamp(v,-1.0,1.0)) -template makeBinOpVector(s:expr)= +template makeBinOpVector(s) = ## implements binary operators ``+``, ``-``, ``*`` and ``/`` for vectors proc s*(a,b:Vector2d):Vector2d {.inline,noInit.} = vector2d(s(a.x,b.x),s(a.y,b.y)) proc s*(a:Vector2d,b:float):Vector2d {.inline,noInit.} = vector2d(s(a.x,b),s(a.y,b)) proc s*(a:float,b:Vector2d):Vector2d {.inline,noInit.} = vector2d(s(a,b.x),s(a,b.y)) -template makeBinOpAssignVector(s:expr)= +template makeBinOpAssignVector(s)= ## implements inplace binary operators ``+=``, ``-=``, ``/=`` and ``*=`` for vectors proc s*(a:var Vector2d,b:Vector2d) {.inline.} = s(a.x,b.x) ; s(a.y,b.y) proc s*(a:var Vector2d,b:float) {.inline.} = s(a.x,b) ; s(a.y,b) @@ -853,5 +853,3 @@ proc degToRad*(deg:float):float {.inline.}= proc radToDeg*(rad:float):float {.inline.}= ## converts `rad` radians to degrees rad * RAD2DEGCONST - - diff --git a/lib/pure/basic3d.nim b/lib/pure/basic3d.nim index f7a9c237c..e2d2464c0 100644 --- a/lib/pure/basic3d.nim +++ b/lib/pure/basic3d.nim @@ -116,7 +116,7 @@ proc safeArccos(v:float):float= ## due to rounding issues return arccos(clamp(v,-1.0,1.0)) -template makeBinOpVector(s:expr)= +template makeBinOpVector(s) = proc s*(a,b:Vector3d):Vector3d {.inline,noInit.} = vector3d(s(a.x,b.x),s(a.y,b.y),s(a.z,b.z)) proc s*(a:Vector3d,b:float):Vector3d {.inline,noInit.} = @@ -124,7 +124,7 @@ template makeBinOpVector(s:expr)= proc s*(a:float,b:Vector3d):Vector3d {.inline,noInit.} = vector3d(s(a,b.x),s(a,b.y),s(a,b.z)) -template makeBinOpAssignVector(s:expr)= +template makeBinOpAssignVector(s) = proc s*(a:var Vector3d,b:Vector3d) {.inline.} = s(a.x,b.x); s(a.y,b.y); s(a.z,b.z) proc s*(a:var Vector3d,b:float) {.inline.} = diff --git a/lib/pure/collections/critbits.nim b/lib/pure/collections/critbits.nim index 519c58653..5f84f3101 100644 --- a/lib/pure/collections/critbits.nim +++ b/lib/pure/collections/critbits.nim @@ -143,15 +143,16 @@ proc `[]=`*[T](c: var CritBitTree[T], key: string, val: T) = var n = rawInsert(c, key) n.val = val -template get[T](c: CritBitTree[T], key: string): T {.immediate.} = +template get[T](c: CritBitTree[T], key: string): T = let n = rawGet(c, key) - if n != nil: result = n.val - else: + if n == nil: when compiles($key): raise newException(KeyError, "key not found: " & $key) else: raise newException(KeyError, "key not found") + n.val + proc `[]`*[T](c: CritBitTree[T], key: string): T {.inline, deprecatedGet.} = ## retrieves the value at ``c[key]``. If `key` is not in `t`, the ## ``KeyError`` exception is raised. One can check with ``hasKey`` whether diff --git a/lib/pure/collections/deques.nim b/lib/pure/collections/deques.nim index 78953228b..1bbe9f1ad 100644 --- a/lib/pure/collections/deques.nim +++ b/lib/pure/collections/deques.nim @@ -33,7 +33,7 @@ ## assert deq.peekLast == a ## ## while deq.len > 0: # checking if the deque is empty -## echo deq.removeLast() +## echo deq.popLast() ## ## Note: For inter thread communication use ## a `Channel <channels.html>`_ instead. diff --git a/lib/pure/collections/intsets.nim b/lib/pure/collections/intsets.nim index 4ecac11be..334e33f2e 100644 --- a/lib/pure/collections/intsets.nim +++ b/lib/pure/collections/intsets.nim @@ -31,16 +31,18 @@ const type PTrunk = ref Trunk - Trunk {.final.} = object + Trunk = object next: PTrunk # all nodes are connected with this pointer key: int # start address at bit 0 bits: array[0..IntsPerTrunk - 1, BitScalar] # a bit vector TrunkSeq = seq[PTrunk] IntSet* = object ## an efficient set of 'int' implemented as a sparse bit set + elems: int # only valid for small numbers counter, max: int head: PTrunk data: TrunkSeq + a: array[0..33, int] # profiling shows that 34 elements are enough {.deprecated: [TIntSet: IntSet, TTrunk: Trunk, TTrunkSeq: TrunkSeq].} @@ -95,101 +97,154 @@ proc intSetPut(t: var IntSet, key: int): PTrunk = proc contains*(s: IntSet, key: int): bool = ## returns true iff `key` is in `s`. - var t = intSetGet(s, `shr`(key, TrunkShift)) - if t != nil: - var u = key and TrunkMask - result = (t.bits[`shr`(u, IntShift)] and `shl`(1, u and IntMask)) != 0 + if s.elems <= s.a.len: + for i in 0..<s.elems: + if s.a[i] == key: return true else: - result = false - -proc incl*(s: var IntSet, key: int) = - ## includes an element `key` in `s`. + var t = intSetGet(s, `shr`(key, TrunkShift)) + if t != nil: + var u = key and TrunkMask + result = (t.bits[`shr`(u, IntShift)] and `shl`(1, u and IntMask)) != 0 + else: + result = false + +proc bitincl(s: var IntSet, key: int) {.inline.} = var t = intSetPut(s, `shr`(key, TrunkShift)) var u = key and TrunkMask t.bits[`shr`(u, IntShift)] = t.bits[`shr`(u, IntShift)] or `shl`(1, u and IntMask) +proc incl*(s: var IntSet, key: int) = + ## includes an element `key` in `s`. + if s.elems <= s.a.len: + for i in 0..<s.elems: + if s.a[i] == key: return + if s.elems < s.a.len: + s.a[s.elems] = key + inc s.elems + return + newSeq(s.data, InitIntSetSize) + s.max = InitIntSetSize-1 + for i in 0..<s.elems: + bitincl(s, s.a[i]) + s.elems = s.a.len + 1 + # fall through: + bitincl(s, key) + proc excl*(s: var IntSet, key: int) = ## excludes `key` from the set `s`. - var t = intSetGet(s, `shr`(key, TrunkShift)) - if t != nil: - var u = key and TrunkMask - t.bits[`shr`(u, IntShift)] = t.bits[`shr`(u, IntShift)] and - not `shl`(1, u and IntMask) + if s.elems <= s.a.len: + for i in 0..<s.elems: + if s.a[i] == key: + s.a[i] = s.a[s.elems-1] + dec s.elems + return + else: + var t = intSetGet(s, `shr`(key, TrunkShift)) + if t != nil: + var u = key and TrunkMask + t.bits[`shr`(u, IntShift)] = t.bits[`shr`(u, IntShift)] and + not `shl`(1, u and IntMask) proc containsOrIncl*(s: var IntSet, key: int): bool = ## returns true if `s` contains `key`, otherwise `key` is included in `s` ## and false is returned. - var t = intSetGet(s, `shr`(key, TrunkShift)) - if t != nil: - var u = key and TrunkMask - result = (t.bits[`shr`(u, IntShift)] and `shl`(1, u and IntMask)) != 0 - if not result: - t.bits[`shr`(u, IntShift)] = t.bits[`shr`(u, IntShift)] or - `shl`(1, u and IntMask) - else: + if s.elems <= s.a.len: + for i in 0..<s.elems: + if s.a[i] == key: + return true incl(s, key) result = false + else: + var t = intSetGet(s, `shr`(key, TrunkShift)) + if t != nil: + var u = key and TrunkMask + result = (t.bits[`shr`(u, IntShift)] and `shl`(1, u and IntMask)) != 0 + if not result: + t.bits[`shr`(u, IntShift)] = t.bits[`shr`(u, IntShift)] or + `shl`(1, u and IntMask) + else: + incl(s, key) + result = false proc initIntSet*: IntSet = ## creates a new int set that is empty. - newSeq(result.data, InitIntSetSize) - result.max = InitIntSetSize-1 + + #newSeq(result.data, InitIntSetSize) + #result.max = InitIntSetSize-1 + result.data = nil + result.max = 0 result.counter = 0 result.head = nil + result.elems = 0 proc clear*(result: var IntSet) = - setLen(result.data, InitIntSetSize) - for i in 0..InitIntSetSize-1: result.data[i] = nil - result.max = InitIntSetSize-1 + #setLen(result.data, InitIntSetSize) + #for i in 0..InitIntSetSize-1: result.data[i] = nil + #result.max = InitIntSetSize-1 + result.data = nil + result.max = 0 result.counter = 0 result.head = nil + result.elems = 0 -proc isNil*(x: IntSet): bool {.inline.} = x.head.isNil +proc isNil*(x: IntSet): bool {.inline.} = x.head.isNil and x.elems == 0 proc assign*(dest: var IntSet, src: IntSet) = ## copies `src` to `dest`. `dest` does not need to be initialized by ## `initIntSet`. - dest.counter = src.counter - dest.max = src.max - newSeq(dest.data, src.data.len) + if src.elems <= src.a.len: + dest.data = nil + dest.max = 0 + dest.counter = src.counter + dest.head = nil + dest.elems = src.elems + dest.a = src.a + else: + dest.counter = src.counter + dest.max = src.max + newSeq(dest.data, src.data.len) - var it = src.head - while it != nil: + var it = src.head + while it != nil: - var h = it.key and dest.max - while dest.data[h] != nil: h = nextTry(h, dest.max) - assert(dest.data[h] == nil) + var h = it.key and dest.max + while dest.data[h] != nil: h = nextTry(h, dest.max) + assert(dest.data[h] == nil) - var n: PTrunk - new(n) - n.next = dest.head - n.key = it.key - n.bits = it.bits - dest.head = n - dest.data[h] = n + var n: PTrunk + new(n) + n.next = dest.head + n.key = it.key + n.bits = it.bits + dest.head = n + dest.data[h] = n - it = it.next + it = it.next iterator items*(s: IntSet): int {.inline.} = ## iterates over any included element of `s`. - var r = s.head - while r != nil: - var i = 0 - while i <= high(r.bits): - var w = r.bits[i] - # taking a copy of r.bits[i] here is correct, because - # modifying operations are not allowed during traversation - var j = 0 - while w != 0: # test all remaining bits for zero - if (w and 1) != 0: # the bit is set! - yield (r.key shl TrunkShift) or (i shl IntShift +% j) - inc(j) - w = w shr 1 - inc(i) - r = r.next - -template dollarImpl(): stmt = + if s.elems <= s.a.len: + for i in 0..<s.elems: + yield s.a[i] + else: + var r = s.head + while r != nil: + var i = 0 + while i <= high(r.bits): + var w = r.bits[i] + # taking a copy of r.bits[i] here is correct, because + # modifying operations are not allowed during traversation + var j = 0 + while w != 0: # test all remaining bits for zero + if (w and 1) != 0: # the bit is set! + yield (r.key shl TrunkShift) or (i shl IntShift +% j) + inc(j) + w = w shr 1 + inc(i) + r = r.next + +template dollarImpl(): untyped = result = "{" for key in items(s): if result.len > 1: result.add(", ") @@ -225,3 +280,9 @@ when isMainModule: ys.sort(cmp[int]) assert ys == @[1, 2, 7, 1056] + var z: IntSet + for i in 0..1000: + incl z, i + for i in 0..1000: + assert i in z + diff --git a/lib/pure/collections/rtarrays.nim b/lib/pure/collections/rtarrays.nim index 89a02553a..3849117a0 100644 --- a/lib/pure/collections/rtarrays.nim +++ b/lib/pure/collections/rtarrays.nim @@ -19,7 +19,7 @@ type L: Natural spart: seq[T] apart: array[ArrayPartSize, T] - UncheckedArray* {.unchecked.}[T] = array[0..100_000_000, T] + UncheckedArray* {.unchecked.}[T] = array[0, T] template usesSeqPart(x): untyped = x.L > ArrayPartSize diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim index 19512d5f4..e8e725aa3 100644 --- a/lib/pure/collections/sequtils.nim +++ b/lib/pure/collections/sequtils.nim @@ -455,7 +455,7 @@ template anyIt*(seq1, pred: untyped): bool = break result -template toSeq*(iter: untyped): untyped {.oldimmediate.} = +template toSeq*(iter: untyped): untyped = ## Transforms any iterator into a sequence. ## ## Example: diff --git a/lib/pure/collections/sharedstrings.nim b/lib/pure/collections/sharedstrings.nim index 10ab30767..a9e194fb4 100644 --- a/lib/pure/collections/sharedstrings.nim +++ b/lib/pure/collections/sharedstrings.nim @@ -9,10 +9,8 @@ ## Shared string support for Nim. -const ArrayDummySize = when defined(cpu16): 10_000 else: 100_000_000 - type - UncheckedCharArray {.unchecked.} = array[0..ArrayDummySize, char] + UncheckedCharArray = UncheckedArray[char] type Buffer = ptr object diff --git a/lib/pure/collections/sharedtables.nim b/lib/pure/collections/sharedtables.nim index de573bcb2..fc50ea41c 100644 --- a/lib/pure/collections/sharedtables.nim +++ b/lib/pure/collections/sharedtables.nim @@ -25,7 +25,7 @@ type counter, dataLen: int lock: Lock -template maxHash(t): expr = t.dataLen-1 +template maxHash(t): untyped = t.dataLen-1 include tableimpl diff --git a/lib/pure/collections/tableimpl.nim b/lib/pure/collections/tableimpl.nim index c0d45c392..eec98fcaf 100644 --- a/lib/pure/collections/tableimpl.nim +++ b/lib/pure/collections/tableimpl.nim @@ -85,7 +85,7 @@ template addImpl(enlarge) {.dirty.} = rawInsert(t, t.data, key, val, hc, j) inc(t.counter) -template maybeRehashPutImpl(enlarge) {.oldimmediate, dirty.} = +template maybeRehashPutImpl(enlarge) {.dirty.} = if mustRehash(t.dataLen, t.counter): enlarge(t) index = rawGetKnownHC(t, key, hc) @@ -93,7 +93,7 @@ template maybeRehashPutImpl(enlarge) {.oldimmediate, dirty.} = rawInsert(t, t.data, key, val, hc, index) inc(t.counter) -template putImpl(enlarge) {.oldimmediate, dirty.} = +template putImpl(enlarge) {.dirty.} = var hc: Hash var index = rawGet(t, key, hc) if index >= 0: t.data[index].val = val diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim index 5b6701a12..01a42efab 100644 --- a/lib/pure/collections/tables.nim +++ b/lib/pure/collections/tables.nim @@ -648,7 +648,7 @@ proc `==`*[A, B](s, t: OrderedTable[A, B]): bool = var nxtt = t.data[ht].next var nxts = s.data[hs].next if isFilled(t.data[ht].hcode) and isFilled(s.data[hs].hcode): - if (s.data[hs].key != t.data[ht].key) and (s.data[hs].val != t.data[ht].val): + if (s.data[hs].key != t.data[ht].key) or (s.data[hs].val != t.data[ht].val): return false ht = nxtt hs = nxts @@ -939,7 +939,7 @@ proc enlarge[A](t: var CountTable[A]) = proc `[]=`*[A](t: var CountTable[A], key: A, val: int) = ## puts a (key, value)-pair into `t`. - assert val > 0 + assert val >= 0 var h = rawGet(t, key) if h >= 0: t.data[h].val = val @@ -1311,3 +1311,17 @@ when isMainModule: assert a == c + block: #6250 + let + a = {3: 1}.toOrderedTable + b = {3: 2}.toOrderedTable + assert((a == b) == false) + assert((b == a) == false) + + block: #6250 + let + a = {3: 2}.toOrderedTable + b = {3: 2}.toOrderedTable + assert((a == b) == true) + assert((b == a) == true) + diff --git a/lib/pure/colors.nim b/lib/pure/colors.nim index f4c027576..4ec76dee0 100644 --- a/lib/pure/colors.nim +++ b/lib/pure/colors.nim @@ -19,18 +19,18 @@ type proc `==` *(a, b: Color): bool {.borrow.} ## compares two colors. -template extract(a: Color, r, g, b: expr) {.immediate.}= +template extract(a: Color, r, g, b: untyped) = var r = a.int shr 16 and 0xff var g = a.int shr 8 and 0xff var b = a.int and 0xff -template rawRGB(r, g, b: int): expr = +template rawRGB(r, g, b: int): Color = Color(r shl 16 or g shl 8 or b) -template colorOp(op: expr) {.immediate.} = +template colorOp(op): Color = extract(a, ar, ag, ab) extract(b, br, bg, bb) - result = rawRGB(op(ar, br), op(ag, bg), op(ab, bb)) + rawRGB(op(ar, br), op(ag, bg), op(ab, bb)) proc satPlus(a, b: int): int {.inline.} = result = a +% b @@ -67,12 +67,12 @@ proc intensity*(a: Color, f: float): Color = if b >% 255: b = 255 result = rawRGB(r, g, b) -template mix*(a, b: Color, fn: expr): expr = +template mix*(a, b: Color, fn: untyped): untyped = ## uses `fn` to mix the colors `a` and `b`. `fn` is invoked for each component ## R, G, and B. This is a template because `fn` should be inlined and the ## compiler cannot inline proc pointers yet. If `fn`'s result is not in the ## range[0..255], it will be saturated to be so. - template `><` (x: expr): expr = + template `><` (x: untyped): untyped = # keep it in the range 0..255 block: var y = x # eval only once diff --git a/lib/pure/concurrency/threadpool.nim b/lib/pure/concurrency/threadpool.nim index f438a85e7..2a0dbd2ca 100644 --- a/lib/pure/concurrency/threadpool.nim +++ b/lib/pure/concurrency/threadpool.nim @@ -409,20 +409,20 @@ proc preferSpawn*(): bool = ## it is not necessary to call this directly; use 'spawnX' instead. result = gSomeReady.counter > 0 -proc spawn*(call: expr): expr {.magic: "Spawn".} +proc spawn*(call: typed): void {.magic: "Spawn".} ## always spawns a new task, so that the 'call' is never executed on ## the calling thread. 'call' has to be proc call 'p(...)' where 'p' ## is gcsafe and has a return type that is either 'void' or compatible ## with ``FlowVar[T]``. -proc pinnedSpawn*(id: ThreadId; call: expr): expr {.magic: "Spawn".} +proc pinnedSpawn*(id: ThreadId; call: typed): void {.magic: "Spawn".} ## always spawns a new task on the worker thread with ``id``, so that ## the 'call' is **always** executed on ## the thread. 'call' has to be proc call 'p(...)' where 'p' ## is gcsafe and has a return type that is either 'void' or compatible ## with ``FlowVar[T]``. -template spawnX*(call: expr): expr = +template spawnX*(call): void = ## spawns a new task if a CPU core is ready, otherwise executes the ## call in the calling thread. Usually it is advised to ## use 'spawn' in order to not block the producer for an unknown @@ -431,7 +431,7 @@ template spawnX*(call: expr): expr = ## with ``FlowVar[T]``. (if preferSpawn(): spawn call else: call) -proc parallel*(body: stmt) {.magic: "Parallel".} +proc parallel*(body: untyped) {.magic: "Parallel".} ## a parallel section can be used to execute a block in parallel. ``body`` ## has to be in a DSL that is a particular subset of the language. Please ## refer to the manual for further information. @@ -530,6 +530,7 @@ proc nimSpawn4(fn: WorkerProc; data: pointer; id: ThreadId) {.compilerProc.} = proc sync*() = ## a simple barrier to wait for all spawn'ed tasks. If you need more elaborate ## waiting, you have to use an explicit barrier. + var toRelease = 0 while true: var allReady = true for i in 0 .. <currentPoolSize: @@ -537,5 +538,9 @@ proc sync*() = allReady = allReady and workersData[i].ready if allReady: break await(gSomeReady) + inc toRelease + + for i in 0 ..< toRelease: + signal(gSomeReady) setup() diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim index 909a2613f..cb4f4f664 100644 --- a/lib/pure/httpclient.nim +++ b/lib/pure/httpclient.nim @@ -1245,6 +1245,8 @@ proc downloadFile*(client: HttpClient | AsyncHttpClient, url: string, filename: string): Future[void] {.multisync.} = ## Downloads ``url`` and saves it to ``filename``. client.getBody = false + defer: + client.getBody = true let resp = await client.get(url) when client is HttpClient: diff --git a/lib/pure/includes/osenv.nim b/lib/pure/includes/osenv.nim new file mode 100644 index 000000000..8d2fc235a --- /dev/null +++ b/lib/pure/includes/osenv.nim @@ -0,0 +1,159 @@ +## Include file that implements 'getEnv' and friends. Do not import it! + +when not declared(ospaths): + {.error: "This is an include file for ospaths.nim!".} + +proc c_getenv(env: cstring): cstring {. + importc: "getenv", header: "<stdlib.h>".} +proc c_putenv(env: cstring): cint {. + importc: "putenv", header: "<stdlib.h>".} + +# Environment handling cannot be put into RTL, because the ``envPairs`` +# iterator depends on ``environment``. + +var + envComputed {.threadvar.}: bool + environment {.threadvar.}: seq[string] + +when defined(windows) and not defined(nimscript): + # because we support Windows GUI applications, things get really + # messy here... + when useWinUnicode: + when defined(cpp): + proc strEnd(cstr: WideCString, c = 0'i32): WideCString {. + importcpp: "(NI16*)wcschr((const wchar_t *)#, #)", header: "<string.h>".} + else: + proc strEnd(cstr: WideCString, c = 0'i32): WideCString {. + importc: "wcschr", header: "<string.h>".} + else: + proc strEnd(cstr: cstring, c = 0'i32): cstring {. + importc: "strchr", header: "<string.h>".} + + proc getEnvVarsC() = + if not envComputed: + environment = @[] + when useWinUnicode: + var + env = getEnvironmentStringsW() + e = env + if e == nil: return # an error occurred + while true: + var eend = strEnd(e) + add(environment, $e) + e = cast[WideCString](cast[ByteAddress](eend)+2) + if eend[1].int == 0: break + discard freeEnvironmentStringsW(env) + else: + var + env = getEnvironmentStringsA() + e = env + if e == nil: return # an error occurred + while true: + var eend = strEnd(e) + add(environment, $e) + e = cast[cstring](cast[ByteAddress](eend)+1) + if eend[1] == '\0': break + discard freeEnvironmentStringsA(env) + envComputed = true + +else: + const + useNSGetEnviron = (defined(macosx) and not defined(ios)) or defined(nimscript) + + when useNSGetEnviron: + # From the manual: + # Shared libraries and bundles don't have direct access to environ, + # which is only available to the loader ld(1) when a complete program + # is being linked. + # The environment routines can still be used, but if direct access to + # environ is needed, the _NSGetEnviron() routine, defined in + # <crt_externs.h>, can be used to retrieve the address of environ + # at runtime. + proc NSGetEnviron(): ptr cstringArray {. + importc: "_NSGetEnviron", header: "<crt_externs.h>".} + else: + var gEnv {.importc: "environ".}: cstringArray + + proc getEnvVarsC() = + # retrieves the variables of char** env of C's main proc + if not envComputed: + environment = @[] + when useNSGetEnviron: + var gEnv = NSGetEnviron()[] + var i = 0 + while true: + if gEnv[i] == nil: break + add environment, $gEnv[i] + inc(i) + envComputed = true + +proc findEnvVar(key: string): int = + getEnvVarsC() + var temp = key & '=' + for i in 0..high(environment): + if startsWith(environment[i], temp): return i + return -1 + +proc getEnv*(key: string): TaintedString {.tags: [ReadEnvEffect].} = + ## Returns the value of the `environment variable`:idx: named `key`. + ## + ## If the variable does not exist, "" is returned. To distinguish + ## whether a variable exists or it's value is just "", call + ## `existsEnv(key)`. + when nimvm: + discard "built into the compiler" + else: + var i = findEnvVar(key) + if i >= 0: + return TaintedString(substr(environment[i], find(environment[i], '=')+1)) + else: + var env = c_getenv(key) + if env == nil: return TaintedString("") + result = TaintedString($env) + +proc existsEnv*(key: string): bool {.tags: [ReadEnvEffect].} = + ## Checks whether the environment variable named `key` exists. + ## Returns true if it exists, false otherwise. + when nimvm: + discard "built into the compiler" + else: + if c_getenv(key) != nil: return true + else: return findEnvVar(key) >= 0 + +proc putEnv*(key, val: string) {.tags: [WriteEnvEffect].} = + ## Sets the value of the `environment variable`:idx: named `key` to `val`. + ## If an error occurs, `EInvalidEnvVar` is raised. + + # Note: by storing the string in the environment sequence, + # we guarantee that we don't free the memory before the program + # ends (this is needed for POSIX compliance). It is also needed so that + # the process itself may access its modified environment variables! + when nimvm: + discard "built into the compiler" + else: + var indx = findEnvVar(key) + if indx >= 0: + environment[indx] = key & '=' & val + else: + add environment, (key & '=' & val) + indx = high(environment) + when defined(windows) and not defined(nimscript): + when useWinUnicode: + var k = newWideCString(key) + var v = newWideCString(val) + if setEnvironmentVariableW(k, v) == 0'i32: raiseOSError(osLastError()) + else: + if setEnvironmentVariableA(key, val) == 0'i32: raiseOSError(osLastError()) + else: + if c_putenv(environment[indx]) != 0'i32: + raiseOSError(osLastError()) + +iterator envPairs*(): tuple[key, value: TaintedString] {.tags: [ReadEnvEffect].} = + ## Iterate over all `environments variables`:idx:. In the first component + ## of the tuple is the name of the current variable stored, in the second + ## its value. + getEnvVarsC() + for i in 0..high(environment): + var p = find(environment[i], '=') + yield (TaintedString(substr(environment[i], 0, p-1)), + TaintedString(substr(environment[i], p+1))) diff --git a/lib/pure/includes/oserr.nim b/lib/pure/includes/oserr.nim new file mode 100644 index 000000000..dbb709f1b --- /dev/null +++ b/lib/pure/includes/oserr.nim @@ -0,0 +1,135 @@ +## Include file that implements 'osErrorMsg' and friends. Do not import it! + +when not declared(ospaths): + {.error: "This is an include file for ospaths.nim!".} + +when not defined(nimscript): + var errno {.importc, header: "<errno.h>".}: cint + + proc c_strerror(errnum: cint): cstring {. + importc: "strerror", header: "<string.h>".} + + when defined(windows): + import winlean + +proc osErrorMsg*(): string {.rtl, extern: "nos$1", deprecated.} = + ## Retrieves the operating system's error flag, ``errno``. + ## On Windows ``GetLastError`` is checked before ``errno``. + ## Returns "" if no error occurred. + ## + ## **Deprecated since version 0.9.4**: use the other ``osErrorMsg`` proc. + + result = "" + when defined(Windows) and not defined(nimscript): + var err = getLastError() + if err != 0'i32: + when useWinUnicode: + var msgbuf: WideCString + if formatMessageW(0x00000100 or 0x00001000 or 0x00000200 or 0x000000FF, + nil, err, 0, addr(msgbuf), 0, nil) != 0'i32: + result = $msgbuf + if msgbuf != nil: localFree(cast[pointer](msgbuf)) + else: + var msgbuf: cstring + if formatMessageA(0x00000100 or 0x00001000 or 0x00000200 or 0x000000FF, + nil, err, 0, addr(msgbuf), 0, nil) != 0'i32: + result = $msgbuf + if msgbuf != nil: localFree(msgbuf) + when not defined(nimscript): + if errno != 0'i32: + result = $c_strerror(errno) + +{.push warning[deprecated]: off.} +proc raiseOSError*(msg: string = "") {.noinline, rtl, extern: "nos$1", + deprecated.} = + ## raises an OSError exception with the given message ``msg``. + ## If ``msg == ""``, the operating system's error flag + ## (``errno``) is converted to a readable error message. On Windows + ## ``GetLastError`` is checked before ``errno``. + ## If no error flag is set, the message ``unknown OS error`` is used. + ## + ## **Deprecated since version 0.9.4**: use the other ``raiseOSError`` proc. + if len(msg) == 0: + var m = osErrorMsg() + raise newException(OSError, if m.len > 0: m else: "unknown OS error") + else: + raise newException(OSError, msg) +{.pop.} + +when not defined(nimfix): + {.deprecated: [osError: raiseOSError].} + +proc `==`*(err1, err2: OSErrorCode): bool {.borrow.} +proc `$`*(err: OSErrorCode): string {.borrow.} + +proc osErrorMsg*(errorCode: OSErrorCode): string = + ## Converts an OS error code into a human readable string. + ## + ## The error code can be retrieved using the ``osLastError`` proc. + ## + ## If conversion fails, or ``errorCode`` is ``0`` then ``""`` will be + ## returned. + ## + ## On Windows, the ``-d:useWinAnsi`` compilation flag can be used to + ## make this procedure use the non-unicode Win API calls to retrieve the + ## message. + result = "" + when defined(nimscript): + discard + elif defined(Windows): + if errorCode != OSErrorCode(0'i32): + when useWinUnicode: + var msgbuf: WideCString + if formatMessageW(0x00000100 or 0x00001000 or 0x00000200, + nil, errorCode.int32, 0, addr(msgbuf), 0, nil) != 0'i32: + result = $msgbuf + if msgbuf != nil: localFree(cast[pointer](msgbuf)) + else: + var msgbuf: cstring + if formatMessageA(0x00000100 or 0x00001000 or 0x00000200, + nil, errorCode.int32, 0, addr(msgbuf), 0, nil) != 0'i32: + result = $msgbuf + if msgbuf != nil: localFree(msgbuf) + else: + if errorCode != OSErrorCode(0'i32): + result = $c_strerror(errorCode.int32) + +proc raiseOSError*(errorCode: OSErrorCode; additionalInfo = "") {.noinline.} = + ## Raises an ``OSError`` exception. The ``errorCode`` will determine the + ## message, ``osErrorMsg`` will be used to get this message. + ## + ## The error code can be retrieved using the ``osLastError`` proc. + ## + ## If the error code is ``0`` or an error message could not be retrieved, + ## the message ``unknown OS error`` will be used. + var e: ref OSError; new(e) + e.errorCode = errorCode.int32 + if additionalInfo.len == 0: + e.msg = osErrorMsg(errorCode) + else: + e.msg = osErrorMsg(errorCode) & "\nAdditional info: " & additionalInfo + if e.msg == "": + e.msg = "unknown OS error" + raise e + +{.push stackTrace:off.} +proc osLastError*(): OSErrorCode = + ## Retrieves the last operating system error code. + ## + ## This procedure is useful in the event when an OS call fails. In that case + ## this procedure will return the error code describing the reason why the + ## OS call failed. The ``OSErrorMsg`` procedure can then be used to convert + ## this code into a string. + ## + ## **Warning**: + ## The behaviour of this procedure varies between Windows and POSIX systems. + ## On Windows some OS calls can reset the error code to ``0`` causing this + ## procedure to return ``0``. It is therefore advised to call this procedure + ## immediately after an OS call fails. On POSIX systems this is not a problem. + when defined(nimscript): + discard + elif defined(windows): + result = OSErrorCode(getLastError()) + else: + result = OSErrorCode(errno) +{.pop.} diff --git a/lib/pure/ioselectors.nim b/lib/pure/ioselectors.nim index cbef5ce0d..ef8072221 100644 --- a/lib/pure/ioselectors.nim +++ b/lib/pure/ioselectors.nim @@ -208,7 +208,7 @@ else: import locks type - SharedArray {.unchecked.}[T] = array[0..100, T] + SharedArray[T] = UncheckedArray[T] proc allocSharedArray[T](nsize: int): ptr SharedArray[T] = result = cast[ptr SharedArray[T]](allocShared0(sizeof(T) * nsize)) diff --git a/lib/pure/memfiles.nim b/lib/pure/memfiles.nim index b6154d8de..d1cf5d9bc 100644 --- a/lib/pure/memfiles.nim +++ b/lib/pure/memfiles.nim @@ -123,7 +123,7 @@ proc open*(filename: string, mode: FileMode = fmRead, result.size = 0 when defined(windows): - template fail(errCode: OSErrorCode, msg: expr) = + template fail(errCode: OSErrorCode, msg: untyped) = rollback() if result.fHandle != 0: discard closeHandle(result.fHandle) if result.mapHandle != 0: discard closeHandle(result.mapHandle) @@ -131,7 +131,7 @@ proc open*(filename: string, mode: FileMode = fmRead, # return false #raise newException(EIO, msg) - template callCreateFile(winApiProc, filename: expr): expr = + template callCreateFile(winApiProc, filename): untyped = winApiProc( filename, # GENERIC_ALL != (GENERIC_READ or GENERIC_WRITE) @@ -198,7 +198,7 @@ proc open*(filename: string, mode: FileMode = fmRead, result.fHandle = INVALID_HANDLE_VALUE else: - template fail(errCode: OSErrorCode, msg: expr) = + template fail(errCode: OSErrorCode, msg: string) = rollback() if result.handle != -1: discard close(result.handle) raiseOSError(errCode) diff --git a/lib/pure/nativesockets.nim b/lib/pure/nativesockets.nim index 7568408a6..1a62c0bf6 100644 --- a/lib/pure/nativesockets.nim +++ b/lib/pure/nativesockets.nim @@ -233,7 +233,7 @@ proc getAddrInfo*(address: string, port: Port, domain: Domain = AF_INET, # OpenBSD doesn't support AI_V4MAPPED and doesn't define the macro AI_V4MAPPED. # FreeBSD doesn't support AI_V4MAPPED but defines the macro. # https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=198092 - when not defined(freebsd) and not defined(openbsd) and not defined(netbsd): + when not defined(freebsd) and not defined(openbsd) and not defined(netbsd) and not defined(android): if domain == AF_INET6: hints.ai_flags = AI_V4MAPPED var gaiResult = getaddrinfo(address, $port, addr(hints), result) diff --git a/lib/pure/net.nim b/lib/pure/net.nim index 629e916fa..215a301b6 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -797,7 +797,7 @@ when false: #defineSsl: ## ## ``AcceptNoClient`` will be returned when no client is currently attempting ## to connect. - template doHandshake(): stmt = + template doHandshake(): untyped = when defineSsl: if server.isSSL: client.setBlocking(false) diff --git a/lib/pure/os.nim b/lib/pure/os.nim index f7bcfb60e..b85181edf 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -29,150 +29,17 @@ else: import ospaths export ospaths -when defined(posix): - when NoFakeVars: - const pathMax = 5000 # doesn't matter really. The concept of PATH_MAX - # doesn't work anymore on modern OSes. - else: - var - pathMax {.importc: "PATH_MAX", header: "<stdlib.h>".}: cint - proc c_remove(filename: cstring): cint {. importc: "remove", header: "<stdio.h>".} proc c_rename(oldname, newname: cstring): cint {. importc: "rename", header: "<stdio.h>".} proc c_system(cmd: cstring): cint {. importc: "system", header: "<stdlib.h>".} -proc c_strerror(errnum: cint): cstring {. - importc: "strerror", header: "<string.h>".} proc c_strlen(a: cstring): cint {. importc: "strlen", header: "<string.h>", noSideEffect.} -proc c_getenv(env: cstring): cstring {. - importc: "getenv", header: "<stdlib.h>".} -proc c_putenv(env: cstring): cint {. - importc: "putenv", header: "<stdlib.h>".} proc c_free(p: pointer) {. importc: "free", header: "<stdlib.h>".} -var errno {.importc, header: "<errno.h>".}: cint - -proc osErrorMsg*(): string {.rtl, extern: "nos$1", deprecated.} = - ## Retrieves the operating system's error flag, ``errno``. - ## On Windows ``GetLastError`` is checked before ``errno``. - ## Returns "" if no error occurred. - ## - ## **Deprecated since version 0.9.4**: use the other ``osErrorMsg`` proc. - - result = "" - when defined(Windows): - var err = getLastError() - if err != 0'i32: - when useWinUnicode: - var msgbuf: WideCString - if formatMessageW(0x00000100 or 0x00001000 or 0x00000200 or 0x000000FF, - nil, err, 0, addr(msgbuf), 0, nil) != 0'i32: - result = $msgbuf - if msgbuf != nil: localFree(cast[pointer](msgbuf)) - else: - var msgbuf: cstring - if formatMessageA(0x00000100 or 0x00001000 or 0x00000200 or 0x000000FF, - nil, err, 0, addr(msgbuf), 0, nil) != 0'i32: - result = $msgbuf - if msgbuf != nil: localFree(msgbuf) - if errno != 0'i32: - result = $os.c_strerror(errno) - -{.push warning[deprecated]: off.} -proc raiseOSError*(msg: string = "") {.noinline, rtl, extern: "nos$1", - deprecated.} = - ## raises an OSError exception with the given message ``msg``. - ## If ``msg == ""``, the operating system's error flag - ## (``errno``) is converted to a readable error message. On Windows - ## ``GetLastError`` is checked before ``errno``. - ## If no error flag is set, the message ``unknown OS error`` is used. - ## - ## **Deprecated since version 0.9.4**: use the other ``raiseOSError`` proc. - if len(msg) == 0: - var m = osErrorMsg() - raise newException(OSError, if m.len > 0: m else: "unknown OS error") - else: - raise newException(OSError, msg) -{.pop.} - -when not defined(nimfix): - {.deprecated: [osError: raiseOSError].} - -proc `==`*(err1, err2: OSErrorCode): bool {.borrow.} -proc `$`*(err: OSErrorCode): string {.borrow.} - -proc osErrorMsg*(errorCode: OSErrorCode): string = - ## Converts an OS error code into a human readable string. - ## - ## The error code can be retrieved using the ``osLastError`` proc. - ## - ## If conversion fails, or ``errorCode`` is ``0`` then ``""`` will be - ## returned. - ## - ## On Windows, the ``-d:useWinAnsi`` compilation flag can be used to - ## make this procedure use the non-unicode Win API calls to retrieve the - ## message. - result = "" - when defined(Windows): - if errorCode != OSErrorCode(0'i32): - when useWinUnicode: - var msgbuf: WideCString - if formatMessageW(0x00000100 or 0x00001000 or 0x00000200, - nil, errorCode.int32, 0, addr(msgbuf), 0, nil) != 0'i32: - result = $msgbuf - if msgbuf != nil: localFree(cast[pointer](msgbuf)) - else: - var msgbuf: cstring - if formatMessageA(0x00000100 or 0x00001000 or 0x00000200, - nil, errorCode.int32, 0, addr(msgbuf), 0, nil) != 0'i32: - result = $msgbuf - if msgbuf != nil: localFree(msgbuf) - else: - if errorCode != OSErrorCode(0'i32): - result = $os.c_strerror(errorCode.int32) - -proc raiseOSError*(errorCode: OSErrorCode; additionalInfo = "") {.noinline.} = - ## Raises an ``OSError`` exception. The ``errorCode`` will determine the - ## message, ``osErrorMsg`` will be used to get this message. - ## - ## The error code can be retrieved using the ``osLastError`` proc. - ## - ## If the error code is ``0`` or an error message could not be retrieved, - ## the message ``unknown OS error`` will be used. - var e: ref OSError; new(e) - e.errorCode = errorCode.int32 - if additionalInfo.len == 0: - e.msg = osErrorMsg(errorCode) - else: - e.msg = osErrorMsg(errorCode) & "\nAdditional info: " & additionalInfo - if e.msg == "": - e.msg = "unknown OS error" - raise e - -{.push stackTrace:off.} -proc osLastError*(): OSErrorCode = - ## Retrieves the last operating system error code. - ## - ## This procedure is useful in the event when an OS call fails. In that case - ## this procedure will return the error code describing the reason why the - ## OS call failed. The ``OSErrorMsg`` procedure can then be used to convert - ## this code into a string. - ## - ## **Warning**: - ## The behaviour of this procedure varies between Windows and POSIX systems. - ## On Windows some OS calls can reset the error code to ``0`` causing this - ## procedure to return ``0``. It is therefore advised to call this procedure - ## immediately after an OS call fails. On POSIX systems this is not a problem. - - when defined(windows): - result = OSErrorCode(getLastError()) - else: - result = OSErrorCode(errno) -{.pop.} when defined(windows): when useWinUnicode: @@ -252,6 +119,60 @@ proc dirExists*(dir: string): bool {.inline.} = ## Synonym for existsDir existsDir(dir) +when not defined(windows): + proc checkSymlink(path: string): bool = + var rawInfo: Stat + if lstat(path, rawInfo) < 0'i32: result = false + else: result = S_ISLNK(rawInfo.st_mode) + +const + ExeExts* = when defined(windows): ["exe", "cmd", "bat"] else: [""] ## \ + ## platform specific file extension for executables. On Windows + ## ``["exe", "cmd", "bat"]``, on Posix ``[""]``. + +proc findExe*(exe: string, followSymlinks: bool = true; + extensions: openarray[string]=ExeExts): string {. + tags: [ReadDirEffect, ReadEnvEffect, ReadIOEffect].} = + ## Searches for `exe` in the current working directory and then + ## in directories listed in the ``PATH`` environment variable. + ## Returns "" if the `exe` cannot be found. `exe` + ## is added the `ExeExts <#ExeExts>`_ file extensions if it has none. + ## If the system supports symlinks it also resolves them until it + ## meets the actual file. This behavior can be disabled if desired. + for ext in extensions: + result = addFileExt(exe, ext) + if existsFile(result): return + var path = string(getEnv("PATH")) + for candidate in split(path, PathSep): + when defined(windows): + var x = (if candidate[0] == '"' and candidate[^1] == '"': + substr(candidate, 1, candidate.len-2) else: candidate) / + exe + else: + var x = expandTilde(candidate) / exe + for ext in extensions: + var x = addFileExt(x, ext) + if existsFile(x): + when not defined(windows): + while followSymlinks: # doubles as if here + if x.checkSymlink: + var r = newString(256) + var len = readlink(x, r, 256) + if len < 0: + raiseOSError(osLastError()) + if len > 256: + r = newString(len+1) + len = readlink(x, r, len) + setLen(r, len) + if isAbsolute(r): + x = r + else: + x = parentDir(x) / r + else: + break + return x + result = "" + proc getLastModificationTime*(file: string): Time {.rtl, extern: "nos$1".} = ## Returns the `file`'s last modification time. when defined(posix): @@ -714,147 +635,6 @@ proc execShellCmd*(command: string): int {.rtl, extern: "nos$1", else: result = c_system(command) -# Environment handling cannot be put into RTL, because the ``envPairs`` -# iterator depends on ``environment``. - -var - envComputed {.threadvar.}: bool - environment {.threadvar.}: seq[string] - -when defined(windows): - # because we support Windows GUI applications, things get really - # messy here... - when useWinUnicode: - when defined(cpp): - proc strEnd(cstr: WideCString, c = 0'i32): WideCString {. - importcpp: "(NI16*)wcschr((const wchar_t *)#, #)", header: "<string.h>".} - else: - proc strEnd(cstr: WideCString, c = 0'i32): WideCString {. - importc: "wcschr", header: "<string.h>".} - else: - proc strEnd(cstr: cstring, c = 0'i32): cstring {. - importc: "strchr", header: "<string.h>".} - - proc getEnvVarsC() = - if not envComputed: - environment = @[] - when useWinUnicode: - var - env = getEnvironmentStringsW() - e = env - if e == nil: return # an error occurred - while true: - var eend = strEnd(e) - add(environment, $e) - e = cast[WideCString](cast[ByteAddress](eend)+2) - if eend[1].int == 0: break - discard freeEnvironmentStringsW(env) - else: - var - env = getEnvironmentStringsA() - e = env - if e == nil: return # an error occurred - while true: - var eend = strEnd(e) - add(environment, $e) - e = cast[cstring](cast[ByteAddress](eend)+1) - if eend[1] == '\0': break - discard freeEnvironmentStringsA(env) - envComputed = true - -else: - const - useNSGetEnviron = defined(macosx) and not defined(ios) - - when useNSGetEnviron: - # From the manual: - # Shared libraries and bundles don't have direct access to environ, - # which is only available to the loader ld(1) when a complete program - # is being linked. - # The environment routines can still be used, but if direct access to - # environ is needed, the _NSGetEnviron() routine, defined in - # <crt_externs.h>, can be used to retrieve the address of environ - # at runtime. - proc NSGetEnviron(): ptr cstringArray {. - importc: "_NSGetEnviron", header: "<crt_externs.h>".} - else: - var gEnv {.importc: "environ".}: cstringArray - - proc getEnvVarsC() = - # retrieves the variables of char** env of C's main proc - if not envComputed: - environment = @[] - when useNSGetEnviron: - var gEnv = NSGetEnviron()[] - var i = 0 - while true: - if gEnv[i] == nil: break - add environment, $gEnv[i] - inc(i) - envComputed = true - -proc findEnvVar(key: string): int = - getEnvVarsC() - var temp = key & '=' - for i in 0..high(environment): - if startsWith(environment[i], temp): return i - return -1 - -proc getEnv*(key: string): TaintedString {.tags: [ReadEnvEffect].} = - ## Returns the value of the `environment variable`:idx: named `key`. - ## - ## If the variable does not exist, "" is returned. To distinguish - ## whether a variable exists or it's value is just "", call - ## `existsEnv(key)`. - var i = findEnvVar(key) - if i >= 0: - return TaintedString(substr(environment[i], find(environment[i], '=')+1)) - else: - var env = c_getenv(key) - if env == nil: return TaintedString("") - result = TaintedString($env) - -proc existsEnv*(key: string): bool {.tags: [ReadEnvEffect].} = - ## Checks whether the environment variable named `key` exists. - ## Returns true if it exists, false otherwise. - if c_getenv(key) != nil: return true - else: return findEnvVar(key) >= 0 - -proc putEnv*(key, val: string) {.tags: [WriteEnvEffect].} = - ## Sets the value of the `environment variable`:idx: named `key` to `val`. - ## If an error occurs, `EInvalidEnvVar` is raised. - - # Note: by storing the string in the environment sequence, - # we guarantee that we don't free the memory before the program - # ends (this is needed for POSIX compliance). It is also needed so that - # the process itself may access its modified environment variables! - var indx = findEnvVar(key) - if indx >= 0: - environment[indx] = key & '=' & val - else: - add environment, (key & '=' & val) - indx = high(environment) - when defined(windows): - when useWinUnicode: - var k = newWideCString(key) - var v = newWideCString(val) - if setEnvironmentVariableW(k, v) == 0'i32: raiseOSError(osLastError()) - else: - if setEnvironmentVariableA(key, val) == 0'i32: raiseOSError(osLastError()) - else: - if c_putenv(environment[indx]) != 0'i32: - raiseOSError(osLastError()) - -iterator envPairs*(): tuple[key, value: TaintedString] {.tags: [ReadEnvEffect].} = - ## Iterate over all `environments variables`:idx:. In the first component - ## of the tuple is the name of the current variable stored, in the second - ## its value. - getEnvVarsC() - for i in 0..high(environment): - var p = find(environment[i], '=') - yield (TaintedString(substr(environment[i], 0, p-1)), - TaintedString(substr(environment[i], p+1))) - # Templates for filtering directories and files when defined(windows): template isDir(f: WIN32_FIND_DATA): bool = @@ -1017,7 +797,7 @@ iterator walkDir*(dir: string; relative=false): tuple[kind: PathComponent, path: y = dir / y var k = pcFile - when defined(linux) or defined(macosx) or defined(bsd): + when defined(linux) or defined(macosx) or defined(bsd) or defined(genode): if x.d_type != DT_UNKNOWN: if x.d_type == DT_DIR: k = pcDir if x.d_type == DT_LNK: @@ -1184,7 +964,9 @@ proc createSymlink*(src, dest: string) = ## Some OS's (such as Microsoft Windows) restrict the creation ## of symlinks to root users (administrators). when defined(Windows): - let flag = dirExists(src).int32 + # 2 is the SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE. This allows + # anyone with developer mode on to create a link + let flag = dirExists(src).int32 or 2 when useWinUnicode: var wSrc = newWideCString(src) var wDst = newWideCString(dest) @@ -1386,7 +1168,7 @@ proc moveDir*(source, dest: string) {.tags: [ReadIOEffect, WriteIOEffect].} = copyDir(source, dest) removeDir(source) -include ospaths +#include ospaths proc expandSymlink*(symlinkPath: string): string = ## Returns a string representing the path to which the symbolic link points. diff --git a/lib/pure/ospaths.nim b/lib/pure/ospaths.nim index fa5342fcf..dcb785c83 100644 --- a/lib/pure/ospaths.nim +++ b/lib/pure/ospaths.nim @@ -7,618 +7,554 @@ # distribution, for details about the copyright. # -# Included by the ``os`` module but a module in its own right for NimScript +# Forwarded by the ``os`` module but a module in its own right for NimScript # support. -when not declared(os): - {.pragma: rtl.} - import strutils - -when defined(nimscript) or (defined(nimdoc) and not declared(os)): - {.pragma: rtl.} - {.push hint[ConvFromXtoItselfNotNeeded]:off.} - -when not declared(getEnv) or defined(nimscript): - type - ReadEnvEffect* = object of ReadIOEffect ## effect that denotes a read - ## from an environment variable - WriteEnvEffect* = object of WriteIOEffect ## effect that denotes a write - ## to an environment variable - - ReadDirEffect* = object of ReadIOEffect ## effect that denotes a read - ## operation from the directory - ## structure - WriteDirEffect* = object of WriteIOEffect ## effect that denotes a write - ## operation to - ## the directory structure - - OSErrorCode* = distinct int32 ## Specifies an OS Error Code. - - {.deprecated: [FReadEnv: ReadEnvEffect, FWriteEnv: WriteEnvEffect, - FReadDir: ReadDirEffect, - FWriteDir: WriteDirEffect, - TOSErrorCode: OSErrorCode - ].} - const - doslikeFileSystem* = defined(windows) or defined(OS2) or defined(DOS) - - when defined(Nimdoc): # only for proper documentation: - const - CurDir* = '.' - ## The constant string used by the operating system to refer to the - ## current directory. - ## - ## For example: '.' for POSIX or ':' for the classic Macintosh. - - ParDir* = ".." - ## The constant string used by the operating system to refer to the - ## parent directory. - ## - ## For example: ".." for POSIX or "::" for the classic Macintosh. - - DirSep* = '/' - ## The character used by the operating system to separate pathname - ## components, for example, '/' for POSIX or ':' for the classic - ## Macintosh. - - AltSep* = '/' - ## An alternative character used by the operating system to separate - ## pathname components, or the same as `DirSep` if only one separator - ## character exists. This is set to '/' on Windows systems - ## where `DirSep` is a backslash. - - PathSep* = ':' - ## The character conventionally used by the operating system to separate - ## search patch components (as in PATH), such as ':' for POSIX - ## or ';' for Windows. - - FileSystemCaseSensitive* = true - ## true if the file system is case sensitive, false otherwise. Used by - ## `cmpPaths` to compare filenames properly. - - ExeExt* = "" - ## The file extension of native executables. For example: - ## "" for POSIX, "exe" on Windows. - - ScriptExt* = "" - ## The file extension of a script file. For example: "" for POSIX, - ## "bat" on Windows. - - DynlibFormat* = "lib$1.so" - ## The format string to turn a filename into a `DLL`:idx: file (also - ## called `shared object`:idx: on some operating systems). +include "system/inclrtl" - elif defined(macos): - const - CurDir* = ':' - ParDir* = "::" - DirSep* = ':' - AltSep* = Dirsep - PathSep* = ',' - FileSystemCaseSensitive* = false - ExeExt* = "" - ScriptExt* = "" - DynlibFormat* = "$1.dylib" - - # MacOS paths - # =========== - # MacOS directory separator is a colon ":" which is the only character not - # allowed in filenames. - # - # A path containing no colon or which begins with a colon is a partial - # path. - # E.g. ":kalle:petter" ":kalle" "kalle" - # - # All other paths are full (absolute) paths. E.g. "HD:kalle:" "HD:" - # When generating paths, one is safe if one ensures that all partial paths - # begin with a colon, and all full paths end with a colon. - # In full paths the first name (e g HD above) is the name of a mounted - # volume. - # These names are not unique, because, for instance, two diskettes with the - # same names could be inserted. This means that paths on MacOS are not - # waterproof. In case of equal names the first volume found will do. - # Two colons "::" are the relative path to the parent. Three is to the - # grandparent etc. - elif doslikeFileSystem: - const - CurDir* = '.' - ParDir* = ".." - DirSep* = '\\' # seperator within paths - AltSep* = '/' - PathSep* = ';' # seperator between paths - FileSystemCaseSensitive* = false - ExeExt* = "exe" - ScriptExt* = "bat" - DynlibFormat* = "$1.dll" - elif defined(PalmOS) or defined(MorphOS): - const - DirSep* = '/' - AltSep* = Dirsep - PathSep* = ';' - ParDir* = ".." - FileSystemCaseSensitive* = false - ExeExt* = "" - ScriptExt* = "" - DynlibFormat* = "$1.prc" - elif defined(RISCOS): - const - DirSep* = '.' - AltSep* = '.' - ParDir* = ".." # is this correct? - PathSep* = ',' - FileSystemCaseSensitive* = true - ExeExt* = "" - ScriptExt* = "" - DynlibFormat* = "lib$1.so" - else: # UNIX-like operating system - const - CurDir* = '.' - ParDir* = ".." - DirSep* = '/' - AltSep* = DirSep - PathSep* = ':' - FileSystemCaseSensitive* = true - ExeExt* = "" - ScriptExt* = "" - DynlibFormat* = when defined(macosx): "lib$1.dylib" else: "lib$1.so" +import strutils + +type + ReadEnvEffect* = object of ReadIOEffect ## effect that denotes a read + ## from an environment variable + WriteEnvEffect* = object of WriteIOEffect ## effect that denotes a write + ## to an environment variable + + ReadDirEffect* = object of ReadIOEffect ## effect that denotes a read + ## operation from the directory + ## structure + WriteDirEffect* = object of WriteIOEffect ## effect that denotes a write + ## operation to + ## the directory structure + + OSErrorCode* = distinct int32 ## Specifies an OS Error Code. +{.deprecated: [FReadEnv: ReadEnvEffect, FWriteEnv: WriteEnvEffect, + FReadDir: ReadDirEffect, + FWriteDir: WriteDirEffect, + TOSErrorCode: OSErrorCode +].} +const + doslikeFileSystem* = defined(windows) or defined(OS2) or defined(DOS) + +when defined(Nimdoc): # only for proper documentation: const - ExtSep* = '.' - ## The character which separates the base filename from the extension; - ## for example, the '.' in ``os.nim``. - - - proc joinPath*(head, tail: string): string {. - noSideEffect, rtl, extern: "nos$1".} = - ## Joins two directory names to one. - ## - ## For example on Unix: - ## - ## .. code-block:: nim - ## joinPath("usr", "lib") - ## - ## results in: - ## - ## .. code-block:: nim - ## "usr/lib" - ## - ## If head is the empty string, tail is returned. If tail is the empty - ## string, head is returned with a trailing path separator. If tail starts - ## with a path separator it will be removed when concatenated to head. Other - ## path separators not located on boundaries won't be modified. More - ## examples on Unix: - ## - ## .. code-block:: nim - ## assert joinPath("usr", "") == "usr/" - ## assert joinPath("", "lib") == "lib" - ## assert joinPath("", "/lib") == "/lib" - ## assert joinPath("usr/", "/lib") == "usr/lib" - if len(head) == 0: - result = tail - elif head[len(head)-1] in {DirSep, AltSep}: - if tail[0] in {DirSep, AltSep}: - result = head & substr(tail, 1) - else: - result = head & tail - else: - if tail[0] in {DirSep, AltSep}: - result = head & tail - else: - result = head & DirSep & tail - - proc joinPath*(parts: varargs[string]): string {.noSideEffect, - rtl, extern: "nos$1OpenArray".} = - ## The same as `joinPath(head, tail)`, but works with any number of - ## directory parts. You need to pass at least one element or the proc - ## will assert in debug builds and crash on release builds. - result = parts[0] - for i in 1..high(parts): - result = joinPath(result, parts[i]) - - proc `/` * (head, tail: string): string {.noSideEffect.} = - ## The same as ``joinPath(head, tail)`` - ## - ## Here are some examples for Unix: - ## - ## .. code-block:: nim - ## assert "usr" / "" == "usr/" - ## assert "" / "lib" == "lib" - ## assert "" / "/lib" == "/lib" - ## assert "usr/" / "/lib" == "usr/lib" - return joinPath(head, tail) - - proc splitPath*(path: string): tuple[head, tail: string] {. - noSideEffect, rtl, extern: "nos$1".} = - ## Splits a directory into (head, tail), so that - ## ``head / tail == path`` (except for edge cases like "/usr"). - ## - ## Examples: - ## - ## .. code-block:: nim - ## splitPath("usr/local/bin") -> ("usr/local", "bin") - ## splitPath("usr/local/bin/") -> ("usr/local/bin", "") - ## splitPath("bin") -> ("", "bin") - ## splitPath("/bin") -> ("", "bin") - ## splitPath("") -> ("", "") - var sepPos = -1 - for i in countdown(len(path)-1, 0): - if path[i] in {DirSep, AltSep}: - sepPos = i - break - if sepPos >= 0: - result.head = substr(path, 0, sepPos-1) - result.tail = substr(path, sepPos+1) + CurDir* = '.' + ## The constant string used by the operating system to refer to the + ## current directory. + ## + ## For example: '.' for POSIX or ':' for the classic Macintosh. + + ParDir* = ".." + ## The constant string used by the operating system to refer to the + ## parent directory. + ## + ## For example: ".." for POSIX or "::" for the classic Macintosh. + + DirSep* = '/' + ## The character used by the operating system to separate pathname + ## components, for example, '/' for POSIX or ':' for the classic + ## Macintosh. + + AltSep* = '/' + ## An alternative character used by the operating system to separate + ## pathname components, or the same as `DirSep` if only one separator + ## character exists. This is set to '/' on Windows systems + ## where `DirSep` is a backslash. + + PathSep* = ':' + ## The character conventionally used by the operating system to separate + ## search patch components (as in PATH), such as ':' for POSIX + ## or ';' for Windows. + + FileSystemCaseSensitive* = true + ## true if the file system is case sensitive, false otherwise. Used by + ## `cmpPaths` to compare filenames properly. + + ExeExt* = "" + ## The file extension of native executables. For example: + ## "" for POSIX, "exe" on Windows. + + ScriptExt* = "" + ## The file extension of a script file. For example: "" for POSIX, + ## "bat" on Windows. + + DynlibFormat* = "lib$1.so" + ## The format string to turn a filename into a `DLL`:idx: file (also + ## called `shared object`:idx: on some operating systems). + +elif defined(macos): + const + CurDir* = ':' + ParDir* = "::" + DirSep* = ':' + AltSep* = Dirsep + PathSep* = ',' + FileSystemCaseSensitive* = false + ExeExt* = "" + ScriptExt* = "" + DynlibFormat* = "$1.dylib" + + # MacOS paths + # =========== + # MacOS directory separator is a colon ":" which is the only character not + # allowed in filenames. + # + # A path containing no colon or which begins with a colon is a partial + # path. + # E.g. ":kalle:petter" ":kalle" "kalle" + # + # All other paths are full (absolute) paths. E.g. "HD:kalle:" "HD:" + # When generating paths, one is safe if one ensures that all partial paths + # begin with a colon, and all full paths end with a colon. + # In full paths the first name (e g HD above) is the name of a mounted + # volume. + # These names are not unique, because, for instance, two diskettes with the + # same names could be inserted. This means that paths on MacOS are not + # waterproof. In case of equal names the first volume found will do. + # Two colons "::" are the relative path to the parent. Three is to the + # grandparent etc. +elif doslikeFileSystem: + const + CurDir* = '.' + ParDir* = ".." + DirSep* = '\\' # seperator within paths + AltSep* = '/' + PathSep* = ';' # seperator between paths + FileSystemCaseSensitive* = false + ExeExt* = "exe" + ScriptExt* = "bat" + DynlibFormat* = "$1.dll" +elif defined(PalmOS) or defined(MorphOS): + const + DirSep* = '/' + AltSep* = Dirsep + PathSep* = ';' + ParDir* = ".." + FileSystemCaseSensitive* = false + ExeExt* = "" + ScriptExt* = "" + DynlibFormat* = "$1.prc" +elif defined(RISCOS): + const + DirSep* = '.' + AltSep* = '.' + ParDir* = ".." # is this correct? + PathSep* = ',' + FileSystemCaseSensitive* = true + ExeExt* = "" + ScriptExt* = "" + DynlibFormat* = "lib$1.so" +else: # UNIX-like operating system + const + CurDir* = '.' + ParDir* = ".." + DirSep* = '/' + AltSep* = DirSep + PathSep* = ':' + FileSystemCaseSensitive* = true + ExeExt* = "" + ScriptExt* = "" + DynlibFormat* = when defined(macosx): "lib$1.dylib" else: "lib$1.so" + +const + ExtSep* = '.' + ## The character which separates the base filename from the extension; + ## for example, the '.' in ``os.nim``. + + +proc joinPath*(head, tail: string): string {. + noSideEffect, rtl, extern: "nos$1".} = + ## Joins two directory names to one. + ## + ## For example on Unix: + ## + ## .. code-block:: nim + ## joinPath("usr", "lib") + ## + ## results in: + ## + ## .. code-block:: nim + ## "usr/lib" + ## + ## If head is the empty string, tail is returned. If tail is the empty + ## string, head is returned with a trailing path separator. If tail starts + ## with a path separator it will be removed when concatenated to head. Other + ## path separators not located on boundaries won't be modified. More + ## examples on Unix: + ## + ## .. code-block:: nim + ## assert joinPath("usr", "") == "usr/" + ## assert joinPath("", "lib") == "lib" + ## assert joinPath("", "/lib") == "/lib" + ## assert joinPath("usr/", "/lib") == "usr/lib" + if len(head) == 0: + result = tail + elif head[len(head)-1] in {DirSep, AltSep}: + if tail[0] in {DirSep, AltSep}: + result = head & substr(tail, 1) else: - result.head = "" - result.tail = path - - proc parentDirPos(path: string): int = - var q = 1 - if len(path) >= 1 and path[len(path)-1] in {DirSep, AltSep}: q = 2 - for i in countdown(len(path)-q, 0): - if path[i] in {DirSep, AltSep}: return i - result = -1 - - proc parentDir*(path: string): string {. - noSideEffect, rtl, extern: "nos$1".} = - ## Returns the parent directory of `path`. - ## - ## This is often the same as the ``head`` result of ``splitPath``. - ## If there is no parent, "" is returned. - ## | Example: ``parentDir("/usr/local/bin") == "/usr/local"``. - ## | Example: ``parentDir("/usr/local/bin/") == "/usr/local"``. - let sepPos = parentDirPos(path) - if sepPos >= 0: - result = substr(path, 0, sepPos-1) + result = head & tail + else: + if tail[0] in {DirSep, AltSep}: + result = head & tail else: - result = "" - - proc tailDir*(path: string): string {. - noSideEffect, rtl, extern: "nos$1".} = - ## Returns the tail part of `path`.. - ## - ## | Example: ``tailDir("/usr/local/bin") == "local/bin"``. - ## | Example: ``tailDir("usr/local/bin/") == "local/bin"``. - ## | Example: ``tailDir("bin") == ""``. - var q = 1 - if len(path) >= 1 and path[len(path)-1] in {DirSep, AltSep}: q = 2 - for i in 0..len(path)-q: - if path[i] in {DirSep, AltSep}: - return substr(path, i+1) + result = head & DirSep & tail + +proc joinPath*(parts: varargs[string]): string {.noSideEffect, + rtl, extern: "nos$1OpenArray".} = + ## The same as `joinPath(head, tail)`, but works with any number of + ## directory parts. You need to pass at least one element or the proc + ## will assert in debug builds and crash on release builds. + result = parts[0] + for i in 1..high(parts): + result = joinPath(result, parts[i]) + +proc `/` * (head, tail: string): string {.noSideEffect.} = + ## The same as ``joinPath(head, tail)`` + ## + ## Here are some examples for Unix: + ## + ## .. code-block:: nim + ## assert "usr" / "" == "usr/" + ## assert "" / "lib" == "lib" + ## assert "" / "/lib" == "/lib" + ## assert "usr/" / "/lib" == "usr/lib" + return joinPath(head, tail) + +proc splitPath*(path: string): tuple[head, tail: string] {. + noSideEffect, rtl, extern: "nos$1".} = + ## Splits a directory into (head, tail), so that + ## ``head / tail == path`` (except for edge cases like "/usr"). + ## + ## Examples: + ## + ## .. code-block:: nim + ## splitPath("usr/local/bin") -> ("usr/local", "bin") + ## splitPath("usr/local/bin/") -> ("usr/local/bin", "") + ## splitPath("bin") -> ("", "bin") + ## splitPath("/bin") -> ("", "bin") + ## splitPath("") -> ("", "") + var sepPos = -1 + for i in countdown(len(path)-1, 0): + if path[i] in {DirSep, AltSep}: + sepPos = i + break + if sepPos >= 0: + result.head = substr(path, 0, sepPos-1) + result.tail = substr(path, sepPos+1) + else: + result.head = "" + result.tail = path + +proc parentDirPos(path: string): int = + var q = 1 + if len(path) >= 1 and path[len(path)-1] in {DirSep, AltSep}: q = 2 + for i in countdown(len(path)-q, 0): + if path[i] in {DirSep, AltSep}: return i + result = -1 + +proc parentDir*(path: string): string {. + noSideEffect, rtl, extern: "nos$1".} = + ## Returns the parent directory of `path`. + ## + ## This is often the same as the ``head`` result of ``splitPath``. + ## If there is no parent, "" is returned. + ## | Example: ``parentDir("/usr/local/bin") == "/usr/local"``. + ## | Example: ``parentDir("/usr/local/bin/") == "/usr/local"``. + let sepPos = parentDirPos(path) + if sepPos >= 0: + result = substr(path, 0, sepPos-1) + else: result = "" - proc isRootDir*(path: string): bool {. - noSideEffect, rtl, extern: "nos$1".} = - ## Checks whether a given `path` is a root directory - result = parentDirPos(path) < 0 - - iterator parentDirs*(path: string, fromRoot=false, inclusive=true): string = - ## Walks over all parent directories of a given `path` - ## - ## If `fromRoot` is set, the traversal will start from the file system root - ## diretory. If `inclusive` is set, the original argument will be included - ## in the traversal. - ## - ## Relative paths won't be expanded by this proc. Instead, it will traverse - ## only the directories appearing in the relative path. - if not fromRoot: - var current = path - if inclusive: yield path - while true: - if current.isRootDir: break - current = current.parentDir - yield current - else: - for i in countup(0, path.len - 2): # ignore the last / - # deal with non-normalized paths such as /foo//bar//baz - if path[i] in {DirSep, AltSep} and - (i == 0 or path[i-1] notin {DirSep, AltSep}): - yield path.substr(0, i) - - if inclusive: yield path - - proc `/../` * (head, tail: string): string {.noSideEffect.} = - ## The same as ``parentDir(head) / tail`` unless there is no parent - ## directory. Then ``head / tail`` is performed instead. - let sepPos = parentDirPos(head) - if sepPos >= 0: - result = substr(head, 0, sepPos-1) / tail - else: - result = head / tail - - proc normExt(ext: string): string = - if ext == "" or ext[0] == ExtSep: result = ext # no copy needed here - else: result = ExtSep & ext - - proc searchExtPos*(path: string): int = - ## Returns index of the '.' char in `path` if it signifies the beginning - ## of extension. Returns -1 otherwise. - # BUGFIX: do not search until 0! .DS_Store is no file extension! - result = -1 - for i in countdown(len(path)-1, 1): +proc tailDir*(path: string): string {. + noSideEffect, rtl, extern: "nos$1".} = + ## Returns the tail part of `path`.. + ## + ## | Example: ``tailDir("/usr/local/bin") == "local/bin"``. + ## | Example: ``tailDir("usr/local/bin/") == "local/bin"``. + ## | Example: ``tailDir("bin") == ""``. + var q = 1 + if len(path) >= 1 and path[len(path)-1] in {DirSep, AltSep}: q = 2 + for i in 0..len(path)-q: + if path[i] in {DirSep, AltSep}: + return substr(path, i+1) + result = "" + +proc isRootDir*(path: string): bool {. + noSideEffect, rtl, extern: "nos$1".} = + ## Checks whether a given `path` is a root directory + result = parentDirPos(path) < 0 + +iterator parentDirs*(path: string, fromRoot=false, inclusive=true): string = + ## Walks over all parent directories of a given `path` + ## + ## If `fromRoot` is set, the traversal will start from the file system root + ## diretory. If `inclusive` is set, the original argument will be included + ## in the traversal. + ## + ## Relative paths won't be expanded by this proc. Instead, it will traverse + ## only the directories appearing in the relative path. + if not fromRoot: + var current = path + if inclusive: yield path + while true: + if current.isRootDir: break + current = current.parentDir + yield current + else: + for i in countup(0, path.len - 2): # ignore the last / + # deal with non-normalized paths such as /foo//bar//baz + if path[i] in {DirSep, AltSep} and + (i == 0 or path[i-1] notin {DirSep, AltSep}): + yield path.substr(0, i) + + if inclusive: yield path + +proc `/../`*(head, tail: string): string {.noSideEffect.} = + ## The same as ``parentDir(head) / tail`` unless there is no parent + ## directory. Then ``head / tail`` is performed instead. + let sepPos = parentDirPos(head) + if sepPos >= 0: + result = substr(head, 0, sepPos-1) / tail + else: + result = head / tail + +proc normExt(ext: string): string = + if ext == "" or ext[0] == ExtSep: result = ext # no copy needed here + else: result = ExtSep & ext + +proc searchExtPos*(path: string): int = + ## Returns index of the '.' char in `path` if it signifies the beginning + ## of extension. Returns -1 otherwise. + # BUGFIX: do not search until 0! .DS_Store is no file extension! + result = -1 + for i in countdown(len(path)-1, 1): + if path[i] == ExtSep: + result = i + break + elif path[i] in {DirSep, AltSep}: + break # do not skip over path + +proc splitFile*(path: string): tuple[dir, name, ext: string] {. + noSideEffect, rtl, extern: "nos$1".} = + ## Splits a filename into (dir, filename, extension). + ## `dir` does not end in `DirSep`. + ## `extension` includes the leading dot. + ## + ## Example: + ## + ## .. code-block:: nim + ## var (dir, name, ext) = splitFile("usr/local/nimc.html") + ## assert dir == "usr/local" + ## assert name == "nimc" + ## assert ext == ".html" + ## + ## If `path` has no extension, `ext` is the empty string. + ## If `path` has no directory component, `dir` is the empty string. + ## If `path` has no filename component, `name` and `ext` are empty strings. + if path.len == 0 or path[path.len-1] in {DirSep, AltSep}: + result = (path, "", "") + else: + var sepPos = -1 + var dotPos = path.len + for i in countdown(len(path)-1, 0): if path[i] == ExtSep: - result = i - break + if dotPos == path.len and i > 0 and + path[i-1] notin {DirSep, AltSep}: dotPos = i elif path[i] in {DirSep, AltSep}: - break # do not skip over path - - proc splitFile*(path: string): tuple[dir, name, ext: string] {. - noSideEffect, rtl, extern: "nos$1".} = - ## Splits a filename into (dir, filename, extension). - ## `dir` does not end in `DirSep`. - ## `extension` includes the leading dot. - ## - ## Example: - ## - ## .. code-block:: nim - ## var (dir, name, ext) = splitFile("usr/local/nimc.html") - ## assert dir == "usr/local" - ## assert name == "nimc" - ## assert ext == ".html" - ## - ## If `path` has no extension, `ext` is the empty string. - ## If `path` has no directory component, `dir` is the empty string. - ## If `path` has no filename component, `name` and `ext` are empty strings. - if path.len == 0 or path[path.len-1] in {DirSep, AltSep}: - result = (path, "", "") - else: - var sepPos = -1 - var dotPos = path.len - for i in countdown(len(path)-1, 0): - if path[i] == ExtSep: - if dotPos == path.len and i > 0 and - path[i-1] notin {DirSep, AltSep}: dotPos = i - elif path[i] in {DirSep, AltSep}: - sepPos = i - break - result.dir = substr(path, 0, sepPos-1) - result.name = substr(path, sepPos+1, dotPos-1) - result.ext = substr(path, dotPos) - - proc extractFilename*(path: string): string {. - noSideEffect, rtl, extern: "nos$1".} = - ## Extracts the filename of a given `path`. This is the same as - ## ``name & ext`` from ``splitFile(path)``. - if path.len == 0 or path[path.len-1] in {DirSep, AltSep}: - result = "" - else: - result = splitPath(path).tail - - - proc changeFileExt*(filename, ext: string): string {. - noSideEffect, rtl, extern: "nos$1".} = - ## Changes the file extension to `ext`. - ## - ## If the `filename` has no extension, `ext` will be added. - ## If `ext` == "" then any extension is removed. - ## `Ext` should be given without the leading '.', because some - ## filesystems may use a different character. (Although I know - ## of none such beast.) - var extPos = searchExtPos(filename) - if extPos < 0: result = filename & normExt(ext) - else: result = substr(filename, 0, extPos-1) & normExt(ext) - - proc addFileExt*(filename, ext: string): string {. - noSideEffect, rtl, extern: "nos$1".} = - ## Adds the file extension `ext` to `filename`, unless - ## `filename` already has an extension. - ## - ## `Ext` should be given without the leading '.', because some - ## filesystems may use a different character. - ## (Although I know of none such beast.) - var extPos = searchExtPos(filename) - if extPos < 0: result = filename & normExt(ext) - else: result = filename - - proc cmpPaths*(pathA, pathB: string): int {. - noSideEffect, rtl, extern: "nos$1".} = - ## Compares two paths. - ## - ## On a case-sensitive filesystem this is done - ## case-sensitively otherwise case-insensitively. Returns: - ## - ## | 0 iff pathA == pathB - ## | < 0 iff pathA < pathB - ## | > 0 iff pathA > pathB - if FileSystemCaseSensitive: - result = cmp(pathA, pathB) - else: - when defined(nimscript): - result = cmpic(pathA, pathB) - elif defined(nimdoc): discard - else: - result = cmpIgnoreCase(pathA, pathB) - - 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. - when doslikeFileSystem: - var len = len(path) - result = (len > 0 and path[0] in {'/', '\\'}) or - (len > 1 and path[0] in {'a'..'z', 'A'..'Z'} and path[1] == ':') - elif defined(macos): - result = path.len > 0 and path[0] != ':' - elif defined(RISCOS): - result = path[0] == '$' - elif defined(posix): - result = path[0] == '/' - - proc unixToNativePath*(path: string, drive=""): string {. - noSideEffect, rtl, extern: "nos$1".} = - ## Converts an UNIX-like path to a native one. - ## - ## On an UNIX system this does nothing. Else it converts - ## '/', '.', '..' to the appropriate things. - ## - ## On systems with a concept of "drives", `drive` is used to determine - ## which drive label to use during absolute path conversion. - ## `drive` defaults to the drive of the current working directory, and is - ## ignored on systems that do not have a concept of "drives". - - when defined(unix): - result = path + sepPos = i + break + result.dir = substr(path, 0, sepPos-1) + result.name = substr(path, sepPos+1, dotPos-1) + result.ext = substr(path, dotPos) + +proc extractFilename*(path: string): string {. + noSideEffect, rtl, extern: "nos$1".} = + ## Extracts the filename of a given `path`. This is the same as + ## ``name & ext`` from ``splitFile(path)``. + if path.len == 0 or path[path.len-1] in {DirSep, AltSep}: + result = "" + else: + result = splitPath(path).tail + + +proc changeFileExt*(filename, ext: string): string {. + noSideEffect, rtl, extern: "nos$1".} = + ## Changes the file extension to `ext`. + ## + ## If the `filename` has no extension, `ext` will be added. + ## If `ext` == "" then any extension is removed. + ## `Ext` should be given without the leading '.', because some + ## filesystems may use a different character. (Although I know + ## of none such beast.) + var extPos = searchExtPos(filename) + if extPos < 0: result = filename & normExt(ext) + else: result = substr(filename, 0, extPos-1) & normExt(ext) + +proc addFileExt*(filename, ext: string): string {. + noSideEffect, rtl, extern: "nos$1".} = + ## Adds the file extension `ext` to `filename`, unless + ## `filename` already has an extension. + ## + ## `Ext` should be given without the leading '.', because some + ## filesystems may use a different character. + ## (Although I know of none such beast.) + var extPos = searchExtPos(filename) + if extPos < 0: result = filename & normExt(ext) + else: result = filename + +proc cmpPaths*(pathA, pathB: string): int {. + noSideEffect, rtl, extern: "nos$1".} = + ## Compares two paths. + ## + ## On a case-sensitive filesystem this is done + ## case-sensitively otherwise case-insensitively. Returns: + ## + ## | 0 iff pathA == pathB + ## | < 0 iff pathA < pathB + ## | > 0 iff pathA > pathB + if FileSystemCaseSensitive: + result = cmp(pathA, pathB) + else: + when defined(nimscript): + result = cmpic(pathA, pathB) + elif defined(nimdoc): discard else: - var start: int - if path[0] == '/': - # an absolute path - when doslikeFileSystem: - if drive != "": - result = drive & ":" & DirSep - else: - result = $DirSep - elif defined(macos): - result = "" # must not start with ':' + result = cmpIgnoreCase(pathA, pathB) + +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. + when doslikeFileSystem: + var len = len(path) + result = (len > 0 and path[0] in {'/', '\\'}) or + (len > 1 and path[0] in {'a'..'z', 'A'..'Z'} and path[1] == ':') + elif defined(macos): + result = path.len > 0 and path[0] != ':' + elif defined(RISCOS): + result = path[0] == '$' + elif defined(posix): + result = path[0] == '/' + +proc unixToNativePath*(path: string, drive=""): string {. + noSideEffect, rtl, extern: "nos$1".} = + ## Converts an UNIX-like path to a native one. + ## + ## On an UNIX system this does nothing. Else it converts + ## '/', '.', '..' to the appropriate things. + ## + ## On systems with a concept of "drives", `drive` is used to determine + ## which drive label to use during absolute path conversion. + ## `drive` defaults to the drive of the current working directory, and is + ## ignored on systems that do not have a concept of "drives". + + when defined(unix): + result = path + else: + var start: int + if path[0] == '/': + # an absolute path + when doslikeFileSystem: + if drive != "": + result = drive & ":" & DirSep else: result = $DirSep - start = 1 - elif path[0] == '.' and path[1] == '/': - # current directory - result = $CurDir - start = 2 + elif defined(macos): + result = "" # must not start with ':' else: - result = "" - start = 0 - - var i = start - while i < len(path): # ../../../ --> :::: - if path[i] == '.' and path[i+1] == '.' and path[i+2] == '/': - # parent directory - when defined(macos): - if result[high(result)] == ':': - add result, ':' - else: - add result, ParDir + result = $DirSep + start = 1 + elif path[0] == '.' and path[1] == '/': + # current directory + result = $CurDir + start = 2 + else: + result = "" + start = 0 + + var i = start + while i < len(path): # ../../../ --> :::: + if path[i] == '.' and path[i+1] == '.' and path[i+2] == '/': + # parent directory + when defined(macos): + if result[high(result)] == ':': + add result, ':' else: - add result, ParDir & DirSep - inc(i, 3) - elif path[i] == '/': - add result, DirSep - inc(i) + add result, ParDir else: - add result, path[i] - inc(i) - -when defined(nimdoc) and not declared(os): - proc getEnv(x: string): string = discard - proc existsFile(x: string): bool = discard - -when declared(getEnv) or defined(nimscript): - proc getHomeDir*(): string {.rtl, extern: "nos$1", - tags: [ReadEnvEffect, ReadIOEffect].} = - ## Returns the home directory of the current user. - ## - ## This proc is wrapped by the expandTilde proc for the convenience of - ## processing paths coming from user configuration files. - when defined(windows): return string(getEnv("USERPROFILE")) & "\\" - else: return string(getEnv("HOME")) & "/" - - proc getConfigDir*(): string {.rtl, extern: "nos$1", - tags: [ReadEnvEffect, ReadIOEffect].} = - ## Returns the config directory of the current user for applications. - ## - ## On non-Windows OSs, this proc conforms to the XDG Base Directory - ## spec. Thus, this proc returns the value of the XDG_CONFIG_DIR environment - ## variable if it is set, and returns the default configuration directory, - ## "~/.config/", otherwise. - ## - ## An OS-dependent trailing slash is always present at the end of the - ## returned string; `\\` on Windows and `/` on all other OSs. - when defined(windows): return string(getEnv("APPDATA")) & "\\" - elif getEnv("XDG_CONFIG_DIR"): return string(getEnv("XDG_CONFIG_DIR")) & "/" - else: return string(getEnv("HOME")) & "/.config/" - - proc getTempDir*(): string {.rtl, extern: "nos$1", - tags: [ReadEnvEffect, ReadIOEffect].} = - ## Returns the temporary directory of the current user for applications to - ## save temporary files in. - when defined(windows): return string(getEnv("TEMP")) & "\\" - else: return "/tmp/" - - proc expandTilde*(path: string): string {. - tags: [ReadEnvEffect, ReadIOEffect].} = - ## Expands a path starting with ``~/`` to a full path. - ## - ## If `path` starts with the tilde character and is followed by `/` or `\\` - ## this proc will return the reminder of the path appended to the result of - ## the getHomeDir() proc, otherwise the input path will be returned without - ## modification. - ## - ## The behaviour of this proc is the same on the Windows platform despite - ## not having this convention. Example: - ## - ## .. code-block:: nim - ## let configFile = expandTilde("~" / "appname.cfg") - ## echo configFile - ## # --> C:\Users\amber\appname.cfg - if len(path) > 1 and path[0] == '~' and (path[1] == '/' or path[1] == '\\'): - result = getHomeDir() / path.substr(2) - else: - result = path - - when not declared(split): - iterator split(s: string, sep: char): string = - var last = 0 - if len(s) > 0: - while last <= len(s): - var first = last - while last < len(s) and s[last] != sep: inc(last) - yield substr(s, first, last-1) - inc(last) - - when not defined(windows) and declared(os): - proc checkSymlink(path: string): bool = - var rawInfo: Stat - if lstat(path, rawInfo) < 0'i32: result = false - else: result = S_ISLNK(rawInfo.st_mode) - - const - ExeExts* = when defined(windows): ["exe", "cmd", "bat"] else: [""] ## \ - ## platform specific file extension for executables. On Windows - ## ``["exe", "cmd", "bat"]``, on Posix ``[""]``. - - proc findExe*(exe: string, followSymlinks: bool = true; - extensions: openarray[string]=ExeExts): string {. - tags: [ReadDirEffect, ReadEnvEffect, ReadIOEffect].} = - ## Searches for `exe` in the current working directory and then - ## in directories listed in the ``PATH`` environment variable. - ## Returns "" if the `exe` cannot be found. `exe` - ## is added the `ExeExts <#ExeExts>`_ file extensions if it has none. - ## If the system supports symlinks it also resolves them until it - ## meets the actual file. This behavior can be disabled if desired. - for ext in extensions: - result = addFileExt(exe, ext) - if existsFile(result): return - var path = string(getEnv("PATH")) - for candidate in split(path, PathSep): - when defined(windows): - var x = (if candidate[0] == '"' and candidate[^1] == '"': - substr(candidate, 1, candidate.len-2) else: candidate) / - exe + add result, ParDir & DirSep + inc(i, 3) + elif path[i] == '/': + add result, DirSep + inc(i) else: - var x = expandTilde(candidate) / exe - for ext in extensions: - var x = addFileExt(x, ext) - if existsFile(x): - when not defined(windows) and declared(os): - while followSymlinks: # doubles as if here - if x.checkSymlink: - var r = newString(256) - var len = readlink(x, r, 256) - if len < 0: - raiseOSError(osLastError()) - if len > 256: - r = newString(len+1) - len = readlink(x, r, len) - setLen(r, len) - if isAbsolute(r): - x = r - else: - x = parentDir(x) / r - else: - break - return x - result = "" - -when defined(nimscript) or (defined(nimdoc) and not declared(os)): - {.pop.} # hint[ConvFromXtoItselfNotNeeded]:off + add result, path[i] + inc(i) + +include "includes/oserr" +when not defined(nimscript): + include "includes/osenv" + +proc getHomeDir*(): string {.rtl, extern: "nos$1", + tags: [ReadEnvEffect, ReadIOEffect].} = + ## Returns the home directory of the current user. + ## + ## This proc is wrapped by the expandTilde proc for the convenience of + ## processing paths coming from user configuration files. + when defined(windows): return string(getEnv("USERPROFILE")) & "\\" + else: return string(getEnv("HOME")) & "/" + +proc getConfigDir*(): string {.rtl, extern: "nos$1", + tags: [ReadEnvEffect, ReadIOEffect].} = + ## Returns the config directory of the current user for applications. + ## + ## On non-Windows OSs, this proc conforms to the XDG Base Directory + ## spec. Thus, this proc returns the value of the XDG_CONFIG_DIR environment + ## variable if it is set, and returns the default configuration directory, + ## "~/.config/", otherwise. + ## + ## An OS-dependent trailing slash is always present at the end of the + ## returned string; `\\` on Windows and `/` on all other OSs. + when defined(windows): return string(getEnv("APPDATA")) & "\\" + elif getEnv("XDG_CONFIG_DIR"): return string(getEnv("XDG_CONFIG_DIR")) & "/" + else: return string(getEnv("HOME")) & "/.config/" + +proc getTempDir*(): string {.rtl, extern: "nos$1", + tags: [ReadEnvEffect, ReadIOEffect].} = + ## Returns the temporary directory of the current user for applications to + ## save temporary files in. + ## + ## **Please do not use this**: On Android, it currently + ## returns ``getHomeDir()``, and on other Unix based systems it can cause + ## security problems too. That said, you can override this implementation + ## by adding ``-d:tempDir=mytempname`` to your compiler invokation. + when defined(tempDir): + const tempDir {.strdefine.}: string = nil + return tempDir + elif defined(windows): return string(getEnv("TEMP")) & "\\" + elif defined(android): return getHomeDir() + else: return "/tmp/" + +proc expandTilde*(path: string): string {. + tags: [ReadEnvEffect, ReadIOEffect].} = + ## Expands a path starting with ``~/`` to a full path. + ## + ## If `path` starts with the tilde character and is followed by `/` or `\\` + ## this proc will return the reminder of the path appended to the result of + ## the getHomeDir() proc, otherwise the input path will be returned without + ## modification. + ## + ## The behaviour of this proc is the same on the Windows platform despite + ## not having this convention. Example: + ## + ## .. code-block:: nim + ## let configFile = expandTilde("~" / "appname.cfg") + ## echo configFile + ## # --> C:\Users\amber\appname.cfg + if len(path) > 1 and path[0] == '~' and (path[1] == '/' or path[1] == '\\'): + result = getHomeDir() / path.substr(2) + else: + result = path diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index 23c8546c4..fa723f593 100644 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -119,7 +119,8 @@ proc execProcess*(command: string, poUsePath, poEvalCommand}): TaintedString {. rtl, extern: "nosp$1", - tags: [ExecIOEffect, ReadIOEffect].} + tags: [ExecIOEffect, ReadIOEffect, + RootEffect].} ## A convenience procedure that executes ``command`` with ``startProcess`` ## and returns its output as a string. ## WARNING: this function uses poEvalCommand by default for backward compatibility. @@ -131,7 +132,8 @@ proc execProcess*(command: string, ## # Note: outp may have an interleave of text from the nim compile ## # and any output from mytestfile when it runs -proc execCmd*(command: string): int {.rtl, extern: "nosp$1", tags: [ExecIOEffect].} +proc execCmd*(command: string): int {.rtl, extern: "nosp$1", tags: [ExecIOEffect, + ReadIOEffect, RootEffect].} ## Executes ``command`` and returns its error code. Standard input, output, ## error streams are inherited from the calling process. This operation ## is also often called `system`:idx:. @@ -145,7 +147,8 @@ proc startProcess*(command: string, args: openArray[string] = [], env: StringTableRef = nil, options: set[ProcessOption] = {poStdErrToStdOut}): - Process {.rtl, extern: "nosp$1", tags: [ExecIOEffect, ReadEnvEffect].} + Process {.rtl, extern: "nosp$1", tags: [ExecIOEffect, ReadEnvEffect, + RootEffect].} ## Starts a process. `Command` is the executable file, `workingDir` is the ## process's working directory. If ``workingDir == ""`` the current directory ## is used. `args` are the command line arguments that are passed to the @@ -170,7 +173,7 @@ proc startProcess*(command: string, proc startCmd*(command: string, options: set[ProcessOption] = { poStdErrToStdOut, poUsePath}): Process {. - tags: [ExecIOEffect, ReadEnvEffect], deprecated.} = + tags: [ExecIOEffect, ReadEnvEffect, RootEffect], deprecated.} = ## Deprecated - use `startProcess` directly. result = startProcess(command=command, options=options + {poEvalCommand}) @@ -721,7 +724,7 @@ elif not defined(useNimRtl): inc(i) type StartProcessData = object - sysCommand: cstring + sysCommand: string sysArgs: cstringArray sysEnv: cstringArray workingDir: cstring @@ -735,13 +738,13 @@ elif not defined(useNimRtl): not defined(useClone) and not defined(linux) when useProcessAuxSpawn: proc startProcessAuxSpawn(data: StartProcessData): Pid {. - tags: [ExecIOEffect, ReadEnvEffect], gcsafe.} + tags: [ExecIOEffect, ReadEnvEffect, ReadDirEffect, RootEffect], gcsafe.} else: proc startProcessAuxFork(data: StartProcessData): Pid {. - tags: [ExecIOEffect, ReadEnvEffect], gcsafe.} + tags: [ExecIOEffect, ReadEnvEffect, ReadDirEffect, RootEffect], gcsafe.} {.push stacktrace: off, profiler: off.} proc startProcessAfterFork(data: ptr StartProcessData) {. - tags: [ExecIOEffect, ReadEnvEffect], cdecl, gcsafe.} + tags: [ExecIOEffect, ReadEnvEffect, ReadDirEffect, RootEffect], cdecl, gcsafe.} {.pop.} proc startProcess(command: string, @@ -762,7 +765,8 @@ elif not defined(useNimRtl): var sysCommand: string var sysArgsRaw: seq[string] if poEvalCommand in options: - sysCommand = "/bin/sh" + const useShPath {.strdefine.} = "/bin/sh" + sysCommand = useShPath sysArgsRaw = @[sysCommand, "-c", command] assert args.len == 0, "`args` has to be empty when using poEvalCommand." else: @@ -784,7 +788,7 @@ elif not defined(useNimRtl): defer: deallocCStringArray(sysEnv) var data: StartProcessData - data.sysCommand = sysCommand + shallowCopy(data.sysCommand, sysCommand) data.sysArgs = sysArgs data.sysEnv = sysEnv data.pStdin = pStdin @@ -949,11 +953,10 @@ elif not defined(useNimRtl): discard fcntl(data.pErrorPipe[writeIdx], F_SETFD, FD_CLOEXEC) if data.optionPoUsePath: - when defined(uClibc): + when defined(uClibc) or defined(linux): # uClibc environment (OpenWrt included) doesn't have the full execvpe - discard execve(data.sysCommand, data.sysArgs, data.sysEnv) - elif defined(linux) and not defined(android): - discard execvpe(data.sysCommand, data.sysArgs, data.sysEnv) + let exe = findExe(data.sysCommand) + discard execve(exe, data.sysArgs, data.sysEnv) else: # MacOSX doesn't have execvpe, so we need workaround. # On MacOSX we can arrive here only from fork, so this is safe: @@ -1264,7 +1267,8 @@ elif not defined(useNimRtl): proc execCmdEx*(command: string, options: set[ProcessOption] = { poStdErrToStdOut, poUsePath}): tuple[ output: TaintedString, - exitCode: int] {.tags: [ExecIOEffect, ReadIOEffect], gcsafe.} = + exitCode: int] {.tags: + [ExecIOEffect, ReadIOEffect, RootEffect], gcsafe.} = ## a convenience proc that runs the `command`, grabs all its output and ## exit code and returns both. ## diff --git a/lib/pure/parsecfg.nim b/lib/pure/parsecfg.nim index 5bdd3bc40..2a5dbc8f8 100644 --- a/lib/pure/parsecfg.nim +++ b/lib/pure/parsecfg.nim @@ -320,9 +320,13 @@ proc rawGetTok(c: var CfgParser, tok: var Token) = tok.literal = "=" of '-': inc(c.bufpos) - if c.buf[c.bufpos] == '-': inc(c.bufpos) - tok.kind = tkDashDash - tok.literal = "--" + if c.buf[c.bufpos] == '-': + inc(c.bufpos) + tok.kind = tkDashDash + tok.literal = "--" + else: + dec(c.bufpos) + getSymbol(c, tok) of ':': tok.kind = tkColon inc(c.bufpos) diff --git a/lib/pure/parsesql.nim b/lib/pure/parsesql.nim index d6fafec08..00d007d01 100644 --- a/lib/pure/parsesql.nim +++ b/lib/pure/parsesql.nim @@ -496,6 +496,7 @@ type nkPrimaryKey, nkForeignKey, nkNotNull, + nkNull, nkStmtList, nkDot, @@ -565,8 +566,13 @@ proc newNode(k: SqlNodeKind, s: string): SqlNode = result.strVal = s proc len*(n: SqlNode): int = - if isNil(n.sons): result = 0 - else: result = n.sons.len + if n.kind in {nkIdent, nkStringLit, nkBitStringLit, nkHexStringLit, + nkIntegerLit, nkNumericLit}: + result = 0 + else: + result = n.sons.len + +proc `[]`*(n: SqlNode; i: int): SqlNode = n.sons[i] proc add*(father, n: SqlNode) = if isNil(father.sons): father.sons = @[] @@ -613,6 +619,9 @@ proc eat(p: var SqlParser, keyw: string) = else: sqlError(p, keyw.toUpper() & " expected") +proc opt(p: var SqlParser, kind: TokKind) = + if p.tok.kind == kind: getTok(p) + proc parseDataType(p: var SqlParser): SqlNode = if isKeyw(p, "enum"): result = newNode(nkEnumDef) @@ -705,7 +714,7 @@ proc primary(p: var SqlParser): SqlNode = result = newNode(nkCall) result.add(a) getTok(p) - while true: + while p.tok.kind != tkParRi: result.add(parseExpr(p)) if p.tok.kind == tkComma: getTok(p) else: break @@ -776,9 +785,19 @@ proc parseConstraint(p: var SqlParser): SqlNode = expectIdent(p) result.add(newNode(nkIdent, p.tok.literal)) getTok(p) - eat(p, "check") + optKeyw(p, "check") result.add(parseExpr(p)) +proc parseParIdentList(p: var SqlParser, father: SqlNode) = + eat(p, tkParLe) + while true: + expectIdent(p) + father.add(newNode(nkIdent, p.tok.literal)) + getTok(p) + if p.tok.kind != tkComma: break + getTok(p) + eat(p, tkParRi) + proc parseColumnConstraints(p: var SqlParser, result: SqlNode) = while true: if isKeyw(p, "default"): @@ -795,6 +814,9 @@ proc parseColumnConstraints(p: var SqlParser, result: SqlNode) = getTok(p) eat(p, "null") result.add(newNode(nkNotNull)) + elif isKeyw(p, "null"): + getTok(p) + result.add(newNode(nkNull)) elif isKeyw(p, "identity"): getTok(p) result.add(newNode(nkIdentity)) @@ -807,6 +829,7 @@ proc parseColumnConstraints(p: var SqlParser, result: SqlNode) = elif isKeyw(p, "constraint"): result.add(parseConstraint(p)) elif isKeyw(p, "unique"): + getTok(p) result.add(newNode(nkUnique)) else: break @@ -829,16 +852,6 @@ proc parseIfNotExists(p: var SqlParser, k: SqlNodeKind): SqlNode = else: result = newNode(k) -proc parseParIdentList(p: var SqlParser, father: SqlNode) = - eat(p, tkParLe) - while true: - expectIdent(p) - father.add(newNode(nkIdent, p.tok.literal)) - getTok(p) - if p.tok.kind != tkComma: break - getTok(p) - eat(p, tkParRi) - proc parseTableConstraint(p: var SqlParser): SqlNode = if isKeyw(p, "primary"): getTok(p) @@ -866,20 +879,34 @@ proc parseTableConstraint(p: var SqlParser): SqlNode = else: sqlError(p, "column definition expected") +proc parseUnique(p: var SqlParser): SqlNode = + result = parseExpr(p) + if result.kind == nkCall: result.kind = nkUnique + proc parseTableDef(p: var SqlParser): SqlNode = result = parseIfNotExists(p, nkCreateTable) expectIdent(p) result.add(newNode(nkIdent, p.tok.literal)) getTok(p) if p.tok.kind == tkParLe: - while true: - getTok(p) - if p.tok.kind == tkIdentifier or p.tok.kind == tkQuotedIdentifier: + getTok(p) + while p.tok.kind != tkParRi: + if isKeyw(p, "constraint"): + result.add parseConstraint(p) + elif isKeyw(p, "primary") or isKeyw(p, "foreign"): + result.add parseTableConstraint(p) + elif isKeyw(p, "unique"): + result.add parseUnique(p) + elif p.tok.kind == tkIdentifier or p.tok.kind == tkQuotedIdentifier: result.add(parseColumnDef(p)) else: result.add(parseTableConstraint(p)) if p.tok.kind != tkComma: break + getTok(p) eat(p, tkParRi) + # skip additional crap after 'create table (...) crap;' + while p.tok.kind notin {tkSemicolon, tkEof}: + getTok(p) proc parseTypeDef(p: var SqlParser): SqlNode = result = parseIfNotExists(p, nkCreateType) @@ -1046,7 +1073,7 @@ proc parseSelect(p: var SqlParser): SqlNode = getTok(p) result.add(n) -proc parseStmt(p: var SqlParser): SqlNode = +proc parseStmt(p: var SqlParser; parent: SqlNode) = if isKeyw(p, "create"): getTok(p) optKeyw(p, "cached") @@ -1058,21 +1085,23 @@ proc parseStmt(p: var SqlParser): SqlNode = optKeyw(p, "unique") optKeyw(p, "hash") if isKeyw(p, "table"): - result = parseTableDef(p) + parent.add parseTableDef(p) elif isKeyw(p, "type"): - result = parseTypeDef(p) + parent.add parseTypeDef(p) elif isKeyw(p, "index"): - result = parseIndexDef(p) + parent.add parseIndexDef(p) else: sqlError(p, "TABLE expected") elif isKeyw(p, "insert"): - result = parseInsert(p) + parent.add parseInsert(p) elif isKeyw(p, "update"): - result = parseUpdate(p) + parent.add parseUpdate(p) elif isKeyw(p, "delete"): - result = parseDelete(p) + parent.add parseDelete(p) elif isKeyw(p, "select"): - result = parseSelect(p) + parent.add parseSelect(p) + elif isKeyw(p, "begin"): + getTok(p) else: sqlError(p, "CREATE expected") @@ -1089,9 +1118,8 @@ proc parse(p: var SqlParser): SqlNode = ## Syntax errors raise an `EInvalidSql` exception. result = newNode(nkStmtList) while p.tok.kind != tkEof: - var s = parseStmt(p) + parseStmt(p, result) eat(p, tkSemicolon) - result.add(s) if result.len == 1: result = result.sons[0] @@ -1147,6 +1175,8 @@ proc ra(n: SqlNode, s: var string, indent: int) = rs(n, s, indent) of nkNotNull: s.add(" not null") + of nkNull: + s.add(" null") of nkDot: ra(n.sons[0], s, indent) s.add(".") @@ -1330,6 +1360,10 @@ proc renderSQL*(n: SqlNode): string = result = "" ra(n, result, 0) +proc `$`*(n: SqlNode): string = + ## an alias for `renderSQL`. + renderSQL(n) + when not defined(testing) and isMainModule: echo(renderSQL(parseSQL(newStringStream(""" CREATE TYPE happiness AS ENUM ('happy', 'very happy', 'ecstatic'); diff --git a/lib/pure/pegs.nim b/lib/pure/pegs.nim index 6a52e2cd5..5ae2d9182 100644 --- a/lib/pure/pegs.nim +++ b/lib/pure/pegs.nim @@ -139,7 +139,7 @@ proc addChoice(dest: var Peg, elem: Peg) = else: add(dest, elem) else: add(dest, elem) -template multipleOp(k: PegKind, localOpt: expr) = +template multipleOp(k: PegKind, localOpt: untyped) = result.kind = k result.sons = @[] for x in items(a): @@ -328,32 +328,32 @@ proc newNonTerminal*(name: string, line, column: int): NonTerminal {. result.line = line result.col = column -template letters*: expr = +template letters*: Peg = ## expands to ``charset({'A'..'Z', 'a'..'z'})`` charSet({'A'..'Z', 'a'..'z'}) -template digits*: expr = +template digits*: Peg = ## expands to ``charset({'0'..'9'})`` charSet({'0'..'9'}) -template whitespace*: expr = +template whitespace*: Peg = ## expands to ``charset({' ', '\9'..'\13'})`` charSet({' ', '\9'..'\13'}) -template identChars*: expr = +template identChars*: Peg = ## expands to ``charset({'a'..'z', 'A'..'Z', '0'..'9', '_'})`` charSet({'a'..'z', 'A'..'Z', '0'..'9', '_'}) -template identStartChars*: expr = +template identStartChars*: Peg = ## expands to ``charset({'A'..'Z', 'a'..'z', '_'})`` charSet({'a'..'z', 'A'..'Z', '_'}) -template ident*: expr = +template ident*: Peg = ## same as ``[a-zA-Z_][a-zA-z_0-9]*``; standard identifier sequence(charSet({'a'..'z', 'A'..'Z', '_'}), *charSet({'a'..'z', 'A'..'Z', '0'..'9', '_'})) -template natural*: expr = +template natural*: Peg = ## same as ``\d+`` +digits @@ -514,10 +514,10 @@ proc bounds*(c: Captures, when not useUnicode: type Rune = char - template fastRuneAt(s, i, ch: expr) = + template fastRuneAt(s, i, ch) = ch = s[i] inc(i) - template runeLenAt(s, i: expr): expr = 1 + template runeLenAt(s, i): untyped = 1 proc isAlpha(a: char): bool {.inline.} = return a in {'a'..'z','A'..'Z'} proc isUpper(a: char): bool {.inline.} = return a in {'A'..'Z'} @@ -735,7 +735,7 @@ proc rawMatch*(s: string, p: Peg, start: int, c: var Captures): int {. else: result = -1 of pkRule, pkList: assert false -template fillMatches(s, caps, c: expr) = +template fillMatches(s, caps, c) = for k in 0..c.ml-1: let startIdx = c.matches[k][0] let endIdx = c.matches[k][1] diff --git a/lib/pure/random.nim b/lib/pure/random.nim index 8a32f7d9a..27fbfad45 100644 --- a/lib/pure/random.nim +++ b/lib/pure/random.nim @@ -101,7 +101,7 @@ proc random*[T](a: openArray[T]): T = ## returns a random element from the openarray `a`. result = a[random(a.low..a.len)] -proc randomize*(seed: int) {.benign.} = +proc randomize*(seed: int64) {.benign.} = ## Initializes the random number generator with a specific seed. state.a0 = ui(seed shr 16) state.a1 = ui(seed and 0xffff) @@ -123,7 +123,7 @@ when not defined(nimscript): proc getMil(t: Time): int {.importcpp: "getTime", nodecl.} randomize(getMil times.getTime()) else: - let time = int(times.epochTime() * 1_000_000_000) + let time = int64(times.epochTime() * 1_000_000_000) randomize(time) {.pop.} diff --git a/lib/pure/streams.nim b/lib/pure/streams.nim index 438b48beb..69f673990 100644 --- a/lib/pure/streams.nim +++ b/lib/pure/streams.nim @@ -411,12 +411,12 @@ when not defined(js): result.writeDataImpl = fsWriteData result.flushImpl = fsFlush - proc newFileStream*(filename: string, mode: FileMode = fmRead): FileStream = + proc newFileStream*(filename: string, mode: FileMode = fmRead, bufSize: int = -1): FileStream = ## creates a new stream from the file named `filename` with the mode `mode`. ## If the file cannot be opened, nil is returned. See the `system ## <system.html>`_ module for a list of available FileMode enums. var f: File - if open(f, filename, mode): result = newFileStream(f) + if open(f, filename, mode, bufSize): result = newFileStream(f) when true: diff --git a/lib/pure/strscans.nim b/lib/pure/strscans.nim index 83b86fd54..bf26d2e59 100644 --- a/lib/pure/strscans.nim +++ b/lib/pure/strscans.nim @@ -187,7 +187,6 @@ overloaded to handle both single characters and sets of character. if scanp(content, idx, +( ~{'\L', '\0'} -> entry.add(peekChar($input))), '\L'): result.add entry - Calling ordinary Nim procs inside the macro is possible: .. code-block:: nim @@ -253,6 +252,30 @@ is performed. for r in collectLinks(body): echo r + +In this example both macros are combined seamlessly in order to maximise +efficiency and perform different checks. + +.. code-block:: nim + + iterator parseIps*(soup: string): string = + ## ipv4 only! + const digits = {'0'..'9'} + var a, b, c, d: int + var buf = "" + var idx = 0 + while idx < soup.len: + if scanp(soup, idx, (`digits`{1,3}, '.', `digits`{1,3}, '.', + `digits`{1,3}, '.', `digits`{1,3}) -> buf.add($_)): + discard buf.scanf("$i.$i.$i.$i", a, b, c, d) + if (a >= 0 and a <= 254) and + (b >= 0 and b <= 254) and + (c >= 0 and c <= 254) and + (d >= 0 and d <= 254): + yield buf + buf.setLen(0) # need to clear `buf` each time, cause it might contain garbage + idx.inc + ]## diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index 346248a25..2f2b89955 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -1551,6 +1551,37 @@ proc replaceWord*(s, sub: string, by = ""): string {.noSideEffect, # copy the rest: add result, substr(s, i) +proc multiReplace*(s: string, replacements: varargs[(string, string)]): string {.noSideEffect.} = + ## Same as replace, but specialized for doing multiple replacements in a single + ## pass through the input string. + ## + ## Calling replace multiple times after each other is inefficient and result in too many allocations + ## follwed by immediate deallocations as portions of the string gets replaced. + ## multiReplace performs all replacements in a single pass. + ## + ## If the resulting string is not longer than the original input string, only a single + ## memory allocation is required. + ## + ## The order of the replacements does matter. Earlier replacements are preferred over later + ## replacements in the argument list. + result = newStringOfCap(s.len) + var i = 0 + var fastChk: set[char] = {} + for tup in replacements: fastChk.incl(tup[0][0]) # Include first character of all replacements + while i < s.len: + block sIteration: + # Assume most chars in s are not candidates for any replacement operation + if s[i] in fastChk: + for tup in replacements: + if s.continuesWith(tup[0], i): + add result, tup[1] + inc(i, tup[0].len) + break sIteration + # No matching replacement found + # copy current character from s + add result, s[i] + inc(i) + proc delete*(s: var string, first, last: int) {.noSideEffect, rtl, extern: "nsuDelete".} = ## Deletes in `s` the characters at position `first` .. `last`. @@ -2324,6 +2355,10 @@ when isMainModule: doAssert " foo\n bar".indent(4, "Q") == "QQQQ foo\nQQQQ bar" + doAssert "abba".multiReplace(("a", "b"), ("b", "a")) == "baab" + doAssert "Hello World.".multiReplace(("ello", "ELLO"), ("World.", "PEOPLE!")) == "HELLO PEOPLE!" + doAssert "aaaa".multiReplace(("a", "aa"), ("aa", "bb")) == "aaaaaaaa" + doAssert isAlphaAscii('r') doAssert isAlphaAscii('A') doAssert(not isAlphaAscii('$')) diff --git a/lib/pure/subexes.nim b/lib/pure/subexes.nim index 351b3c086..9d807abd4 100644 --- a/lib/pure/subexes.nim +++ b/lib/pure/subexes.nim @@ -46,12 +46,12 @@ type num, i, lineLen: int {.deprecated: [TFormatParser: FormatParser].} -template call(x: stmt) {.immediate.} = +template call(x: untyped): untyped = p.i = i x i = p.i -template callNoLineLenTracking(x: stmt) {.immediate.} = +template callNoLineLenTracking(x: untyped): untyped = let oldLineLen = p.lineLen p.i = i x diff --git a/lib/pure/terminal.nim b/lib/pure/terminal.nim index 87c663c3d..871ac5d39 100644 --- a/lib/pure/terminal.nim +++ b/lib/pure/terminal.nim @@ -578,7 +578,7 @@ template styledEchoProcessArg(f: File, cmd: TerminalCmd) = when cmd == resetStyle: resetAttributes(f) -macro styledWriteLine*(f: File, m: varargs[expr]): stmt = +macro styledWriteLine*(f: File, m: varargs[typed]): untyped = ## Similar to ``writeLine``, but treating terminal style arguments specially. ## When some argument is ``Style``, ``set[Style]``, ``ForegroundColor``, ## ``BackgroundColor`` or ``TerminalCmd`` then it is not sent directly to @@ -614,16 +614,13 @@ macro styledWriteLine*(f: File, m: varargs[expr]): stmt = result.add(newCall(bindSym"write", f, newStrLitNode("\n"))) if reset: result.add(newCall(bindSym"resetAttributes", f)) -macro callStyledEcho(args: varargs[expr]): stmt = +macro styledEcho*(args: varargs[untyped]): untyped = + ## Echoes styles arguments to stdout using ``styledWriteLine``. result = newCall(bindSym"styledWriteLine") result.add(bindSym"stdout") - for arg in children(args[0][1]): + for arg in children(args): result.add(arg) -template styledEcho*(args: varargs[expr]): expr = - ## Echoes styles arguments to stdout using ``styledWriteLine``. - callStyledEcho(args) - proc getch*(): char = ## Read a single character from the terminal, blocking until it is entered. ## The character is not printed to the terminal. diff --git a/lib/pure/times.nim b/lib/pure/times.nim index 1bda94d14..03054e2d9 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -222,6 +222,12 @@ proc toSeconds*(time: Time): float {.tags: [], raises: [], benign.} proc `-`*(a, b: Time): int64 {. rtl, extern: "ntDiffTime", tags: [], raises: [], noSideEffect, benign.} ## computes the difference of two calendar times. Result is in seconds. + ## .. code-block:: nim + ## + ## let a = fromSeconds(1_000_000_000) + ## let b = fromSeconds(1_500_000_000) + ## echo initInterval(seconds=int(b - a)) + ## # (milliseconds: 0, seconds: 20, minutes: 53, hours: 0, days: 5787, months: 0, years: 0) proc `<`*(a, b: Time): bool {. rtl, extern: "ntLtTime", tags: [], raises: [], noSideEffect.} = @@ -301,6 +307,11 @@ proc `+`*(ti1, ti2: TimeInterval): TimeInterval = result.years = carryO + ti1.years + ti2.years proc `-`*(ti: TimeInterval): TimeInterval = + ## Reverses a time interval + ## .. code-block:: nim + ## + ## let day = -initInterval(hours=24) + ## echo day # -> (milliseconds: 0, seconds: 0, minutes: 0, hours: 0, days: -1, months: 0, years: 0) result = TimeInterval( milliseconds: -ti.milliseconds, seconds: -ti.seconds, @@ -313,6 +324,12 @@ proc `-`*(ti: TimeInterval): TimeInterval = proc `-`*(ti1, ti2: TimeInterval): TimeInterval = ## Subtracts TimeInterval ``ti1`` from ``ti2``. + ## .. code-block:: nim + ## + ## let a = fromSeconds(1_000_000_000) + ## let b = fromSeconds(1_500_000_000) + ## echo b.toTimeInterval - a.toTimeInterval + ## # (milliseconds: 0, seconds: -40, minutes: -6, hours: 1, days: -2, months: -2, years: 16) result = ti1 + (-ti2) proc isLeapYear*(year: int): bool = diff --git a/lib/pure/unittest.nim b/lib/pure/unittest.nim index 1ea7b8545..28691fcb4 100644 --- a/lib/pure/unittest.nim +++ b/lib/pure/unittest.nim @@ -13,7 +13,24 @@ ## ## The test status and name is printed after any output or traceback. ## -## Example: +## Tests can be nested, however failure of a nested test will not mark the +## parent test as failed. Setup and teardown are inherited. Setup can be +## overridden locally. +## +## Compiled test files return the number of failed test as exit code, while +## ``nim c -r <testfile.nim>`` exits with 0 or 1 +## +## Running a single test +## --------------------- +## +## Simply specify the test name as a command line argument. +## +## .. code:: +## +## nim c -r test "my super awesome test name" +## +## Example +## ------- ## ## .. code:: nim ## @@ -42,16 +59,9 @@ ## discard v[4] ## ## echo "suite teardown: run once after the tests" -## -## -## Tests can be nested, however failure of a nested test will not mark the -## parent test as failed. Setup and teardown are inherited. Setup can be -## overridden locally. -## Compiled test files return the number of failed test as exit code, while -## nim c -r <testfile.nim> exits with 0 or 1 import - macros, strutils, streams, times + macros, strutils, streams, times, sets when declared(stdout): import os @@ -111,6 +121,7 @@ var checkpoints {.threadvar.}: seq[string] formatters {.threadvar.}: seq[OutputFormatter] + testsToRun {.threadvar.}: HashSet[string] when declared(stdout): abortOnError = existsEnv("NIMTEST_ABORT_ON_ERROR") @@ -290,12 +301,22 @@ method suiteEnded*(formatter: JUnitOutputFormatter) = formatter.stream.writeLine("\t</testsuite>") proc shouldRun(testName: string): bool = - result = true + if testsToRun.len == 0: + return true -proc ensureFormattersInitialized() = + result = testName in testsToRun + +proc ensureInitialized() = if formatters == nil: formatters = @[OutputFormatter(defaultConsoleFormatter())] + if not testsToRun.isValid: + testsToRun.init() + when declared(os): + # Read tests to run from the command line. + for i in 1 .. paramCount(): + testsToRun.incl(paramStr(i)) + # These two procs are added as workarounds for # https://github.com/nim-lang/Nim/issues/5549 proc suiteEnded() = @@ -335,7 +356,7 @@ template suite*(name, body) {.dirty.} = ## [Suite] test suite for addition ## [OK] 2 + 2 = 4 ## [OK] (2 + -2) != 4 - bind formatters, ensureFormattersInitialized, suiteEnded + bind formatters, ensureInitialized, suiteEnded block: template setup(setupBody: untyped) {.dirty, used.} = @@ -348,7 +369,7 @@ template suite*(name, body) {.dirty.} = let testSuiteName {.used.} = name - ensureFormattersInitialized() + ensureInitialized() try: for formatter in formatters: formatter.suiteStarted(name) @@ -370,9 +391,9 @@ template test*(name, body) {.dirty.} = ## .. code-block:: ## ## [OK] roses are red - bind shouldRun, checkpoints, formatters, ensureFormattersInitialized, testEnded + bind shouldRun, checkpoints, formatters, ensureInitialized, testEnded - ensureFormattersInitialized() + ensureInitialized() if shouldRun(name): checkpoints = @[] @@ -433,14 +454,14 @@ template fail* = ## fail() ## ## outputs "Checkpoint A" before quitting. - bind ensureFormattersInitialized + bind ensureInitialized when declared(testStatusIMPL): testStatusIMPL = FAILED else: programResult += 1 - ensureFormattersInitialized() + ensureInitialized() # var stackTrace: string = nil for formatter in formatters: diff --git a/lib/pure/uri.nim b/lib/pure/uri.nim index c7e0ed1da..d8e4ed52f 100644 --- a/lib/pure/uri.nim +++ b/lib/pure/uri.nim @@ -81,7 +81,7 @@ proc parsePath(uri: string, i: var int, result: var Uri) = i.inc parseUntil(uri, result.path, {'?', '#'}, i) # The 'mailto' scheme's PATH actually contains the hostname/username - if result.scheme.toLower == "mailto": + if cmpIgnoreCase(result.scheme, "mailto") == 0: parseAuthority(result.path, result) result.path.setLen(0) @@ -215,7 +215,7 @@ proc combine*(base: Uri, reference: Uri): Uri = ## let bar = combine(parseUri("http://example.com/foo/bar/"), parseUri("baz")) ## assert bar.path == "/foo/bar/baz" - template setAuthority(dest, src: expr): stmt = + template setAuthority(dest, src): untyped = dest.hostname = src.hostname dest.username = src.username dest.port = src.port diff --git a/lib/pure/xmltree.nim b/lib/pure/xmltree.nim index 7cfb62157..45696c80c 100644 --- a/lib/pure/xmltree.nim +++ b/lib/pure/xmltree.nim @@ -338,7 +338,7 @@ proc xmlConstructor(e: NimNode): NimNode {.compileTime.} = else: result = newCall("newXmlTree", toStrLit(a)) -macro `<>`*(x: expr): expr {.immediate.} = +macro `<>`*(x: untyped): untyped = ## Constructor macro for XML. Example usage: ## ## .. code-block:: nim diff --git a/lib/system.nim b/lib/system.nim index 8f653c1e0..9efa850ed 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -80,9 +80,10 @@ type `nil` {.magic: "Nil".} expr* {.magic: Expr, deprecated.} ## meta type to denote an expression (for templates) - ## **Deprecated** since version 0.15. Use ``untyped`` instead. + ## **Deprecated** since version 0.15. Use ``untyped`` instead. stmt* {.magic: Stmt, deprecated.} ## meta type to denote a statement (for templates) - ## **Deprecated** since version 0.15. Use ``typed`` instead. + ## **Deprecated** since version 0.15. Use ``typed`` instead. + void* {.magic: "VoidType".} ## meta type to denote the absence of any type auto* {.magic: Expr.} ## meta type for automatic type determination any* = distinct auto ## meta type for any supported type @@ -230,9 +231,22 @@ proc reset*[T](obj: var T) {.magic: "Reset", noSideEffect.} ## resets an object `obj` to its initial (binary zero) value. This needs to ## be called before any possible `object branch transition`:idx:. -# for low and high the return type T may not be correct, but -# we handle that with compiler magic in semLowHigh() -proc high*[T](x: T): T {.magic: "High", noSideEffect.} +type + range*{.magic: "Range".}[T] ## Generic type to construct range types. + array*{.magic: "Array".}[I, T] ## Generic type to construct + ## fixed-length arrays. + openArray*{.magic: "OpenArray".}[T] ## Generic type to construct open arrays. + ## Open arrays are implemented as a + ## pointer to the array data and a + ## length field. + varargs*{.magic: "Varargs".}[T] ## Generic type to construct a varargs type. + seq*{.magic: "Seq".}[T] ## Generic type to construct sequences. + set*{.magic: "Set".}[T] ## Generic type to construct bit sets. + + UncheckedArray* {.unchecked.}[T] = array[0, T] + ## Array with no bounds checking + +proc high*[T: Ordinal](x: T): T {.magic: "High", noSideEffect.} ## returns the highest possible index of an array, a sequence, a string or ## the highest possible value of an ordinal value `x`. As a special ## semantic rule, `x` may also be a type identifier. @@ -244,8 +258,21 @@ proc high*[T](x: T): T {.magic: "High", noSideEffect.} ## high(2) #=> 9223372036854775807 ## high(int) #=> 9223372036854775807 +proc high*[T: Ordinal](x: typeDesc[T]): T {.magic: "High", noSideEffect.} +proc high*[T](x: openArray[T]): int {.magic: "High", noSideEffect.} +proc high*[I, T](x: array[I, T]): I {.magic: "High", noSideEffect.} +proc high*[I, T](x: typeDesc[array[I, T]]): I {.magic: "High", noSideEffect.} +proc high*(x: cstring): int {.magic: "High", noSideEffect.} +proc high*(x: string): int {.magic: "High", noSideEffect.} + +proc low*[T: Ordinal](x: typeDesc[T]): T {.magic: "Low", noSideEffect.} +proc low*[T](x: openArray[T]): int {.magic: "Low", noSideEffect.} +proc low*[I, T](x: array[I, T]): I {.magic: "Low", noSideEffect.} proc low*[T](x: T): T {.magic: "Low", noSideEffect.} - ## returns the lowest possible index of an array, a sequence, a string or +proc low*[I, T](x: typeDesc[array[I, T]]): I {.magic: "Low", noSideEffect.} +proc low*(x: cstring): int {.magic: "Low", noSideEffect.} +proc low*(x: string): int {.magic: "Low", noSideEffect.} +## returns the lowest possible index of an array, a sequence, a string or ## the lowest possible value of an ordinal value `x`. As a special ## semantic rule, `x` may also be a type identifier. ## @@ -255,18 +282,6 @@ proc low*[T](x: T): T {.magic: "Low", noSideEffect.} ## low(2) #=> -9223372036854775808 ## low(int) #=> -9223372036854775808 -type - range*{.magic: "Range".}[T] ## Generic type to construct range types. - array*{.magic: "Array".}[I, T] ## Generic type to construct - ## fixed-length arrays. - openArray*{.magic: "OpenArray".}[T] ## Generic type to construct open arrays. - ## Open arrays are implemented as a - ## pointer to the array data and a - ## length field. - varargs*{.magic: "Varargs".}[T] ## Generic type to construct a varargs type. - seq*{.magic: "Seq".}[T] ## Generic type to construct sequences. - set*{.magic: "Set".}[T] ## Generic type to construct bit sets. - when defined(nimArrIdx): # :array|openarray|string|seq|cstring|tuple proc `[]`*[I: Ordinal;T](a: T; i: I): T {. @@ -380,8 +395,6 @@ include "system/inclrtl" const NoFakeVars* = defined(nimscript) ## true if the backend doesn't support \ ## "fake variables" like 'var EBADF {.importc.}: cint'. -const ArrayDummySize = when defined(cpu16): 10_000 else: 100_000_000 - when not defined(JS): type TGenericSeq {.compilerproc, pure, inheritable.} = object @@ -389,10 +402,9 @@ when not defined(JS): when defined(gogc): elemSize: int PGenericSeq {.exportc.} = ptr TGenericSeq - UncheckedCharArray {.unchecked.} = array[0..ArrayDummySize, char] # len and space without counting the terminating zero: NimStringDesc {.compilerproc, final.} = object of TGenericSeq - data: UncheckedCharArray + data: UncheckedArray[char] NimString = ptr NimStringDesc when not defined(JS) and not defined(nimscript): @@ -413,7 +425,7 @@ type ## is an int type ranging from one to the maximum value ## of an int. This type is often useful for documentation and debugging. - RootObj* {.exportc: "TNimObject", inheritable.} = + RootObj* {.compilerProc, inheritable.} = object ## the root of Nim's object hierarchy. Objects should ## inherit from RootObj or one of its descendants. However, ## objects that have no ancestor are allowed. @@ -421,7 +433,7 @@ type RootEffect* {.compilerproc.} = object of RootObj ## \ ## base effect class; each effect should - ## inherit from `TEffect` unless you know what + ## inherit from `RootEffect` unless you know what ## you doing. TimeEffect* = object of RootEffect ## Time effect. IOEffect* = object of RootEffect ## IO effect. @@ -1174,6 +1186,8 @@ proc `is` *[T, S](x: T, y: S): bool {.magic: "Is", noSideEffect.} template `isnot` *(x, y: untyped): untyped = not (x is y) ## Negated version of `is`. Equivalent to ``not(x is y)``. +proc `of` *[T, S](x: typeDesc[T], y: typeDesc[S]): bool {.magic: "Of", noSideEffect.} +proc `of` *[T, S](x: T, y: typeDesc[S]): bool {.magic: "Of", noSideEffect.} proc `of` *[T, S](x: T, y: S): bool {.magic: "Of", noSideEffect.} ## Checks if `x` has a type of `y` ## @@ -1312,7 +1326,7 @@ const hostCPU* {.magic: "HostCPU".}: string = "" ## a string that describes the host CPU. Possible values: ## "i386", "alpha", "powerpc", "powerpc64", "powerpc64el", "sparc", - ## "amd64", "mips", "mipsel", "arm", "arm64". + ## "amd64", "mips", "mipsel", "arm", "arm64", "mips64", "mips64el". seqShallowFlag = low(int) @@ -1599,8 +1613,7 @@ type # these work for most platforms: culonglong* {.importc: "unsigned long long", nodecl.} = uint64 ## This is the same as the type ``unsigned long long`` in *C*. - cstringArray* {.importc: "char**", nodecl.} = ptr - array[0..ArrayDummySize, cstring] + cstringArray* {.importc: "char**", nodecl.} = ptr UncheckedArray[cstring] ## This is binary compatible to the type ``char**`` in *C*. The array's ## high value is large enough to disable bounds checking in practice. ## Use `cstringArrayToSeq` to convert it into a ``seq[string]``. @@ -1951,30 +1964,34 @@ iterator countdown*[T](a, b: T, step = 1): T {.inline.} = yield res dec(res, step) -template countupImpl(incr: untyped) {.oldimmediate, dirty.} = +iterator countup*[S, T](a: S, b: T, step = 1): T {.inline.} = + ## Counts from ordinal value `a` up to `b` (inclusive) with the given + ## step count. `S`, `T` may be any ordinal type, `step` may only + ## be positive. **Note**: This fails to count to ``high(int)`` if T = int for + ## efficiency reasons. when T is IntLikeForCount: var res = int(a) while res <= int(b): yield T(res) - incr + inc(res, step) else: var res: T = T(a) while res <= b: yield res - incr - -iterator countup*[S, T](a: S, b: T, step = 1): T {.inline.} = - ## Counts from ordinal value `a` up to `b` (inclusive) with the given - ## step count. `S`, `T` may be any ordinal type, `step` may only - ## be positive. **Note**: This fails to count to ``high(int)`` if T = int for - ## efficiency reasons. - countupImpl: - inc(res, step) + inc(res, step) iterator `..`*[S, T](a: S, b: T): T {.inline.} = ## An alias for `countup`. - countupImpl: - inc(res) + when T is IntLikeForCount: + var res = int(a) + while res <= int(b): + yield T(res) + inc(res) + else: + var res: T = T(a) + while res <= b: + yield res + inc(res) iterator `||`*[S, T](a: S, b: T, annotation=""): T {. inline, magic: "OmpParFor", sideEffect.} = @@ -2551,7 +2568,7 @@ const NimStackTrace = compileOption("stacktrace") template coroutinesSupportedPlatform(): bool = when defined(sparc) or defined(ELATE) or compileOption("gc", "v2") or - defined(boehmgc) or defined(gogc) or defined(nogc) or defined(gcStack) or + defined(boehmgc) or defined(gogc) or defined(nogc) or defined(gcRegions) or defined(gcMarkAndSweep): false else: @@ -2752,10 +2769,10 @@ when not defined(JS): #and not defined(nimscript): {.push stack_trace: off, profiler:off.} when hasAlloc: - when not defined(gcStack): + when not defined(gcRegions): proc initGC() {.gcsafe.} when not defined(boehmgc) and not defined(useMalloc) and - not defined(gogc) and not defined(gcStack): + not defined(gogc) and not defined(gcRegions): proc initAllocator() {.inline.} proc initStackBottom() {.inline, compilerproc.} = @@ -3040,7 +3057,8 @@ when not defined(JS): #and not defined(nimscript): ## creates a NULL terminated cstringArray from `a`. The result has to ## be freed with `deallocCStringArray` after it's not needed anymore. result = cast[cstringArray](alloc0((a.len+1) * sizeof(cstring))) - let x = cast[ptr array[0..ArrayDummySize, string]](a) + + let x = cast[ptr UncheckedArray[string]](a) for i in 0 .. a.high: result[i] = cast[cstring](alloc0(x[i].len+1)) copyMem(result[i], addr(x[i][0]), x[i].len) @@ -3269,7 +3287,7 @@ when not defined(JS): #and not defined(nimscript): proc finished*[T: proc](x: T): bool {.noSideEffect, inline.} = ## can be used to determine if a first class iterator has finished. {.emit: """ - `result` = *((NI*) `x`.ClE_0) < 0; + `result` = ((NI*) `x`.ClE_0)[1] < 0; """.} elif defined(JS): @@ -3752,7 +3770,7 @@ when hasAlloc: proc locals*(): RootObj {.magic: "Plugin", noSideEffect.} = ## generates a tuple constructor expression listing all the local variables ## in the current scope. This is quite fast as it does not rely - ## on any debug or runtime information. Note that in constrast to what + ## on any debug or runtime information. Note that in contrast to what ## the official signature says, the return type is not ``RootObj`` but a ## tuple of a structure that depends on the current scope. Example: ## diff --git a/lib/system/atomics.nim b/lib/system/atomics.nim index 885b01621..8c3801687 100644 --- a/lib/system/atomics.nim +++ b/lib/system/atomics.nim @@ -172,7 +172,7 @@ elif defined(vcc) and hasThreadSupport: header: "<intrin.h>".} else: proc addAndFetch*(p: ptr int, val: int): int {. - importcpp: "_InterlockedExchangeAdd(static_cast<NI volatile *>(#), #)", + importcpp: "_InterlockedExchangeAdd(reinterpret_cast<LONG volatile *>(#), static_cast<LONG>(#))", header: "<intrin.h>".} else: when sizeof(int) == 8: diff --git a/lib/system/cellsets.nim b/lib/system/cellsets.nim index 776a2b7ec..f26cb86ab 100644 --- a/lib/system/cellsets.nim +++ b/lib/system/cellsets.nim @@ -30,13 +30,12 @@ type key: ByteAddress # start address at bit 0 bits: array[BitIndex, int] # a bit vector - PPageDescArray = ptr array[0..1000_000, PPageDesc] + PPageDescArray = ptr UncheckedArray[PPageDesc] CellSet {.final, pure.} = object counter, max: int head: PPageDesc data: PPageDescArray - - PCellArray = ptr array[0..100_000_000, PCell] + PCellArray = ptr UncheckedArray[PCell] CellSeq {.final, pure.} = object len, cap: int d: PCellArray diff --git a/lib/system/channels.nim b/lib/system/channels.nim index 572f12e84..1b90e245f 100644 --- a/lib/system/channels.nim +++ b/lib/system/channels.nim @@ -22,7 +22,7 @@ when not declared(NimString): type pbytes = ptr array[0.. 0xffff, byte] RawChannel {.pure, final.} = object ## msg queue for a thread - rd, wr, count, mask: int + rd, wr, count, mask, maxItems: int data: pbytes lock: SysLock cond: SysCond @@ -37,11 +37,12 @@ type const ChannelDeadMask = -2 -proc initRawChannel(p: pointer) = +proc initRawChannel(p: pointer, maxItems: int) = var c = cast[PRawChannel](p) initSysLock(c.lock) initSysCond(c.cond) c.mask = -1 + c.maxItems = maxItems proc deinitRawChannel(p: pointer) = var c = cast[PRawChannel](p) @@ -203,28 +204,41 @@ proc rawRecv(q: PRawChannel, data: pointer, typ: PNimType) = storeAux(data, addr(q.data[q.rd * typ.size]), typ, q, mLoad) q.rd = (q.rd + 1) and q.mask -template lockChannel(q: expr, action: stmt) {.immediate.} = +template lockChannel(q, action): untyped = acquireSys(q.lock) action releaseSys(q.lock) -template sendImpl(q: expr) {.immediate.} = +proc sendImpl(q: PRawChannel, typ: PNimType, msg: pointer, noBlock: bool): bool = if q.mask == ChannelDeadMask: sysFatal(DeadThreadError, "cannot send message; thread died") acquireSys(q.lock) - var typ = cast[PNimType](getTypeInfo(msg)) - rawSend(q, unsafeAddr(msg), typ) + if q.maxItems > 0: + # Wait until count is less than maxItems + if noBlock and q.count >= q.maxItems: + releaseSys(q.lock) + return + + while q.count >= q.maxItems: + waitSysCond(q.cond, q.lock) + + rawSend(q, msg, typ) q.elemType = typ releaseSys(q.lock) signalSysCond(q.cond) + result = true -proc send*[TMsg](c: var Channel[TMsg], msg: TMsg) = +proc send*[TMsg](c: var Channel[TMsg], msg: TMsg) {.inline.} = ## sends a message to a thread. `msg` is deeply copied. - var q = cast[PRawChannel](addr(c)) - sendImpl(q) + discard sendImpl(cast[PRawChannel](addr c), cast[PNimType](getTypeInfo(msg)), unsafeAddr(msg), false) + +proc trySend*[TMsg](c: var Channel[TMsg], msg: TMsg): bool {.inline.} = + ## Tries to send a message to a thread. `msg` is deeply copied. Doesn't block. + ## Returns `false` if the message was not sent because number of pending items + ## in the cannel exceeded `maxItems`. + sendImpl(cast[PRawChannel](addr c), cast[PNimType](getTypeInfo(msg)), unsafeAddr(msg), true) proc llRecv(q: PRawChannel, res: pointer, typ: PNimType) = - # to save space, the generic is as small as possible q.ready = true while q.count <= 0: waitSysCond(q.cond, q.lock) @@ -233,6 +247,9 @@ proc llRecv(q: PRawChannel, res: pointer, typ: PNimType) = releaseSys(q.lock) sysFatal(ValueError, "cannot receive message of wrong type") rawRecv(q, res, typ) + if q.maxItems > 0 and q.count == q.maxItems - 1: + # Parent thread is awaiting in send. Wake it up. + signalSysCond(q.cond) proc recv*[TMsg](c: var Channel[TMsg]): TMsg = ## receives a message from the channel `c`. This blocks until @@ -267,9 +284,11 @@ proc peek*[TMsg](c: var Channel[TMsg]): int = else: result = -1 -proc open*[TMsg](c: var Channel[TMsg]) = - ## opens a channel `c` for inter thread communication. - initRawChannel(addr(c)) +proc open*[TMsg](c: var Channel[TMsg], maxItems: int = 0) = + ## opens a channel `c` for inter thread communication. The `send` operation + ## will block until number of unprocessed items is less than `maxItems`. + ## For unlimited queue set `maxItems` to 0. + initRawChannel(addr(c), maxItems) proc close*[TMsg](c: var Channel[TMsg]) = ## closes a channel `c` and frees its associated resources. diff --git a/lib/system/deepcopy.nim b/lib/system/deepcopy.nim index e12f08842..65ba2278c 100644 --- a/lib/system/deepcopy.nim +++ b/lib/system/deepcopy.nim @@ -148,11 +148,11 @@ proc genericDeepCopyAux(dest, src: pointer, mt: PNimType; tab: var PtrTable) = let realType = x.typ sysAssert realType == mt, " types do differ" # this version should work for any possible GC: - let size = if mt.base.kind == tyObject: cast[ptr PNimType](s2)[].size else: mt.base.size - let z = newObj(mt, size) + let typ = if mt.base.kind == tyObject: cast[ptr PNimType](s2)[] else: mt.base + let z = newObj(mt, typ.size) unsureAsgnRef(cast[PPointer](dest), z) tab.put(s2, z) - genericDeepCopyAux(z, s2, mt.base, tab) + genericDeepCopyAux(z, s2, typ, tab) else: unsureAsgnRef(cast[PPointer](dest), z) of tyPtr: diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim index 6c163f711..db4d70613 100644 --- a/lib/system/excpt.nim +++ b/lib/system/excpt.nim @@ -293,11 +293,11 @@ proc raiseExceptionAux(e: ref Exception) = quitOrDebug() else: # ugly, but avoids heap allocations :-) - template xadd(buf, s, slen: expr) = + template xadd(buf, s, slen) = if L + slen < high(buf): copyMem(addr(buf[L]), cstring(s), slen) inc L, slen - template add(buf, s: expr) = + template add(buf, s) = xadd(buf, s, s.len) var buf: array[0..2000, char] var L = 0 @@ -404,7 +404,8 @@ when not defined(noSignalHandler): GC_enable() else: var msg: cstring - template asgn(y: expr) = msg = y + template asgn(y) = + msg = y processSignal(sign, asgn) showErrorMessage(msg) when defined(endb): dbgAborting = true diff --git a/lib/system/gc.nim b/lib/system/gc.nim index e37f601b4..80aa5cf1b 100644 --- a/lib/system/gc.nim +++ b/lib/system/gc.nim @@ -238,21 +238,6 @@ proc nimGCunref(p: pointer) {.compilerProc.} = include gc_common -proc prepareDealloc(cell: PCell) = - when useMarkForDebug: - gcAssert(cell notin gch.marked, "Cell still alive!") - let t = cell.typ - if t.finalizer != nil: - # the finalizer could invoke something that - # allocates memory; this could trigger a garbage - # collection. Since we are already collecting we - # prevend recursive entering here by a lock. - # XXX: we should set the cell's children to nil! - inc(gch.recGcLock) - (cast[Finalizer](t.finalizer))(cellToUsr(cell)) - dec(gch.recGcLock) - decTypeSize(cell, t) - template beforeDealloc(gch: var GcHeap; c: PCell; msg: typed) = when false: for i in 0..gch.decStack.len-1: @@ -274,6 +259,9 @@ proc nimGCunrefNoCycle(p: pointer) {.compilerProc, inline.} = sysAssert(allocInv(gch.region), "end nimGCunrefNoCycle 2") sysAssert(allocInv(gch.region), "end nimGCunrefNoCycle 5") +proc nimGCunrefRC1(p: pointer) {.compilerProc, inline.} = + decRef(usrToCell(p)) + proc asgnRef(dest: PPointer, src: pointer) {.compilerProc, inline.} = # the code generator calls this proc! gcAssert(not isOnStack(dest), "asgnRef") @@ -754,8 +742,8 @@ proc gcMark(gch: var GcHeap, p: pointer) {.inline.} = #[ This method is conditionally marked with an attribute so that it gets ignored by the LLVM ASAN - (Address SANitizer) intrumentation as it will raise false errors due to the implementation of - garbage collection that is used by Nim. For more information, please see the documentation of + (Address SANitizer) intrumentation as it will raise false errors due to the implementation of + garbage collection that is used by Nim. For more information, please see the documentation of `CLANG_NO_SANITIZE_ADDRESS` in `lib/nimbase.h`. ]# proc markStackAndRegisters(gch: var GcHeap) {.noinline, cdecl, codegenDecl: "CLANG_NO_SANITIZE_ADDRESS $# $#$#".} = @@ -920,11 +908,13 @@ when not defined(useNimRtl): else: inc(gch.recGcLock) proc GC_enable() = - if gch.recGcLock > 0: - when hasThreadSupport and hasSharedHeap: - discard atomicDec(gch.recGcLock, 1) - else: - dec(gch.recGcLock) + if gch.recGcLock <= 0: + raise newException(AssertionError, + "API usage error: GC_enable called but GC is already enabled") + when hasThreadSupport and hasSharedHeap: + discard atomicDec(gch.recGcLock, 1) + else: + dec(gch.recGcLock) proc GC_setStrategy(strategy: GC_Strategy) = discard @@ -945,7 +935,6 @@ when not defined(useNimRtl): release(gch) proc GC_getStatistics(): string = - GC_disable() result = "[GC] total memory: " & $(getTotalMem()) & "\n" & "[GC] occupied memory: " & $(getOccupiedMem()) & "\n" & "[GC] stack scans: " & $gch.stat.stackScans & "\n" & @@ -961,6 +950,5 @@ when not defined(useNimRtl): result = result & "[GC] stack " & stack.bottom.repr & "[GC] max stack size " & cast[pointer](stack.maxStackSize).repr & "\n" else: result = result & "[GC] max stack size: " & $gch.stat.maxStackSize & "\n" - GC_enable() {.pop.} # profiler: off, stackTrace: off diff --git a/lib/system/gc_common.nim b/lib/system/gc_common.nim index cd03d2a54..220331e96 100644 --- a/lib/system/gc_common.nim +++ b/lib/system/gc_common.nim @@ -351,3 +351,35 @@ else: # ---------------------------------------------------------------------------- # end of non-portable code # ---------------------------------------------------------------------------- + +proc prepareDealloc(cell: PCell) = + when declared(useMarkForDebug): + when useMarkForDebug: + gcAssert(cell notin gch.marked, "Cell still alive!") + let t = cell.typ + if t.finalizer != nil: + # the finalizer could invoke something that + # allocates memory; this could trigger a garbage + # collection. Since we are already collecting we + # prevend recursive entering here by a lock. + # XXX: we should set the cell's children to nil! + inc(gch.recGcLock) + (cast[Finalizer](t.finalizer))(cellToUsr(cell)) + dec(gch.recGcLock) + decTypeSize(cell, t) + +proc deallocHeap*(runFinalizers = true; allowGcAfterwards = true) = + ## Frees the thread local heap. Runs every finalizer if ``runFinalizers``` + ## is true. If ``allowGcAfterwards`` is true, a minimal amount of allocation + ## happens to ensure the GC can continue to work after the call + ## to ``deallocHeap``. + if runFinalizers: + for x in allObjects(gch.region): + if isCell(x): + # cast to PCell is correct here: + var c = cast[PCell](x) + prepareDealloc(c) + deallocOsPages(gch.region) + zeroMem(addr gch.region, sizeof(gch.region)) + if allowGcAfterwards: + initGC() diff --git a/lib/system/gc_ms.nim b/lib/system/gc_ms.nim index a97e974a1..e03140d05 100644 --- a/lib/system/gc_ms.nim +++ b/lib/system/gc_ms.nim @@ -221,18 +221,6 @@ when defined(nimGcRefLeak): include gc_common -proc prepareDealloc(cell: PCell) = - if cell.typ.finalizer != nil: - # the finalizer could invoke something that - # allocates memory; this could trigger a garbage - # collection. Since we are already collecting we - # prevend recursive entering here by a lock. - # XXX: we should set the cell's children to nil! - inc(gch.recGcLock) - (cast[Finalizer](cell.typ.finalizer))(cellToUsr(cell)) - dec(gch.recGcLock) - decTypeSize cell, cell.typ - proc initGC() = when not defined(useNimRtl): gch.cycleThreshold = InitialThreshold @@ -506,11 +494,13 @@ when not defined(useNimRtl): else: inc(gch.recGcLock) proc GC_enable() = - if gch.recGcLock > 0: - when hasThreadSupport and hasSharedHeap: - atomicDec(gch.recGcLock, 1) - else: - dec(gch.recGcLock) + if gch.recGcLock <= 0: + raise newException(AssertionError, + "API usage error: GC_enable called but GC is already enabled") + when hasThreadSupport and hasSharedHeap: + atomicDec(gch.recGcLock, 1) + else: + dec(gch.recGcLock) proc GC_setStrategy(strategy: GC_Strategy) = discard @@ -530,7 +520,6 @@ when not defined(useNimRtl): release(gch) proc GC_getStatistics(): string = - GC_disable() result = "[GC] total memory: " & $getTotalMem() & "\n" & "[GC] occupied memory: " & $getOccupiedMem() & "\n" & "[GC] collections: " & $gch.stat.collections & "\n" & @@ -542,6 +531,5 @@ when not defined(useNimRtl): result = result & "[GC] stack " & stack.bottom.repr & "[GC] max stack size " & $stack.maxStackSize & "\n" else: result = result & "[GC] max stack size: " & $gch.stat.maxStackSize & "\n" - GC_enable() {.pop.} diff --git a/lib/system/gc_stack.nim b/lib/system/gc_regions.nim index e7b9f65a7..e9efbdfb0 100644 --- a/lib/system/gc_stack.nim +++ b/lib/system/gc_regions.nim @@ -44,10 +44,6 @@ type typ: PNimType nextFinal: ptr ObjHeader # next object with finalizer - Hole = object # stacks can have holes. Otherwise 'growObj' would be insane. - zeroTyp: pointer # overlaid with 'typ' field. Always 'nil'. - size: int # size of the free slot - Chunk = ptr BaseChunk BaseChunk = object next: Chunk @@ -55,7 +51,15 @@ type head, tail: ptr ObjHeader # first and last object in chunk that # has a finalizer attached to it +const + MaxSmallObject = 128 + type + FreeEntry = ptr object + next: FreeEntry + SizedFreeEntry = ptr object + next: SizedFreeEntry + size: int StackPtr = object bump: pointer remaining: int @@ -66,12 +70,21 @@ type bump: pointer head, tail: Chunk nextChunkSize, totalSize: int - hole: ptr Hole # we support individual freeing + freeLists: array[MaxSmallObject div MemAlign, FreeEntry] + holes: SizedFreeEntry when hasThreadSupport: lock: SysLock + SeqHeader = object # minor hack ahead: Since we know that seqs + # and strings cannot have finalizers, we use the field + # instead for a 'region' field so that they can grow + # and shrink safely. + typ: PNimType + region: ptr MemRegion + var tlRegion {.threadVar.}: MemRegion +# tempStrRegion {.threadVar.}: MemRegion # not yet used template withRegion*(r: MemRegion; body: untyped) = let oldRegion = tlRegion @@ -85,6 +98,9 @@ template withRegion*(r: MemRegion; body: untyped) = template inc(p: pointer, s: int) = p = cast[pointer](cast[int](p) +% s) +template dec(p: pointer, s: int) = + p = cast[pointer](cast[int](p) -% s) + template `+!`(p: pointer, s: int): pointer = cast[pointer](cast[int](p) +% s) @@ -128,7 +144,22 @@ proc allocSlowPath(r: var MemRegion; size: int) = r.tail = fresh r.remaining = s - sizeof(BaseChunk) -proc alloc(r: var MemRegion; size: int): pointer {.inline.} = +proc alloc(r: var MemRegion; size: int): pointer = + if size <= MaxSmallObject: + var it = r.freeLists[size div MemAlign] + if it != nil: + r.freeLists[size div MemAlign] = it.next + return pointer(it) + else: + var it = r.holes + var prev: SizedFreeEntry = nil + while it != nil: + if it.size >= size: + if prev != nil: prev.next = it.next + else: r.holes = it.next + return pointer(it) + prev = it + it = it.next if size > r.remaining: allocSlowPath(r, size) sysAssert(size <= r.remaining, "size <= r.remaining") @@ -145,12 +176,23 @@ proc runFinalizers(c: Chunk) = (cast[Finalizer](it.typ.finalizer))(it+!sizeof(ObjHeader)) it = it.nextFinal -when false: - proc dealloc(r: var MemRegion; p: pointer) = - let it = cast[ptr ObjHeader](p-!sizeof(ObjHeader)) - if it.typ != nil and it.typ.finalizer != nil: - (cast[Finalizer](it.typ.finalizer))(p) - it.typ = nil +proc dealloc(r: var MemRegion; p: pointer; size: int) = + let it = cast[ptr ObjHeader](p-!sizeof(ObjHeader)) + if it.typ != nil and it.typ.finalizer != nil: + (cast[Finalizer](it.typ.finalizer))(p) + it.typ = nil + # it is benefitial to not use the free lists here: + if r.bump -! size == p: + dec r.bump, size + elif size <= MaxSmallObject: + let it = cast[FreeEntry](p) + it.next = r.freeLists[size div MemAlign] + r.freeLists[size div MemAlign] = it + else: + let it = cast[SizedFreeEntry](p) + it.size = size + it.next = r.holes + r.holes = it proc deallocAll(r: var MemRegion; head: Chunk) = var it = head @@ -175,12 +217,15 @@ template computeRemaining(r): untyped = proc setObstackPtr*(r: var MemRegion; sp: StackPtr) = # free everything after 'sp': - if sp.current != nil: + if sp.current.next != nil: deallocAll(r, sp.current.next) sp.current.next = nil - else: - deallocAll(r, r.head) - r.head = nil + # better leak this memory than be sorry: + for i in 0..high(r.freeLists): r.freeLists[i] = nil + r.holes = nil + #else: + # deallocAll(r, r.head) + # r.head = nil r.bump = sp.bump r.tail = sp.current r.remaining = sp.remaining @@ -191,17 +236,28 @@ proc deallocAll*() = tlRegion.deallocAll() proc deallocOsPages(r: var MemRegion) = r.deallocAll() -proc joinRegion*(dest: var MemRegion; src: MemRegion) = - # merging is not hard. - if dest.head.isNil: - dest.head = src.head - else: - dest.tail.next = src.head - dest.tail = src.tail - dest.bump = src.bump - dest.remaining = src.remaining - dest.nextChunkSize = max(dest.nextChunkSize, src.nextChunkSize) - inc dest.totalSize, src.totalSize +template withScratchRegion*(body: untyped) = + var scratch: MemRegion + let oldRegion = tlRegion + tlRegion = scratch + try: + body + finally: + tlRegion = oldRegion + deallocAll(scratch) + +when false: + proc joinRegion*(dest: var MemRegion; src: MemRegion) = + # merging is not hard. + if dest.head.isNil: + dest.head = src.head + else: + dest.tail.next = src.head + dest.tail = src.tail + dest.bump = src.bump + dest.remaining = src.remaining + dest.nextChunkSize = max(dest.nextChunkSize, src.nextChunkSize) + inc dest.totalSize, src.totalSize proc isOnHeap*(r: MemRegion; p: pointer): bool = # the tail chunk is the largest, so check it first. It's also special @@ -213,159 +269,6 @@ proc isOnHeap*(r: MemRegion; p: pointer): bool = if it >= p and p <= it+!it.size: return true it = it.next -when false: - # essential feature for later: copy data over from one region to another - - proc isInteriorPointer(r: MemRegion; p: pointer): pointer = - discard " we cannot patch stack pointers anyway!" - - type - PointerStackChunk = object - next, prev: ptr PointerStackChunk - len: int - data: array[128, pointer] - - template head(s: PointerStackChunk): untyped = s.prev - template tail(s: PointerStackChunk): untyped = s.next - - include chains - - proc push(r: var MemRegion; s: var PointerStackChunk; x: pointer) = - if s.len < high(s.data): - s.data[s.len] = x - inc s.len - else: - let fresh = cast[ptr PointerStackChunk](alloc(r, sizeof(PointerStackChunk))) - fresh.len = 1 - fresh.data[0] = x - fresh.next = nil - fresh.prev = nil - append(s, fresh) - - - proc genericDeepCopyAux(dr: var MemRegion; stack: var PointerStackChunk; - dest, src: pointer, mt: PNimType) {.benign.} - proc genericDeepCopyAux(dr: var MemRegion; stack: var PointerStackChunk; - dest, src: pointer, n: ptr TNimNode) {.benign.} = - var - d = cast[ByteAddress](dest) - s = cast[ByteAddress](src) - case n.kind - of nkSlot: - genericDeepCopyAux(cast[pointer](d +% n.offset), - cast[pointer](s +% n.offset), n.typ) - of nkList: - for i in 0..n.len-1: - genericDeepCopyAux(dest, src, n.sons[i]) - of nkCase: - var dd = selectBranch(dest, n) - var m = selectBranch(src, n) - # reset if different branches are in use; note different branches also - # imply that's not self-assignment (``x = x``)! - if m != dd and dd != nil: - genericResetAux(dest, dd) - copyMem(cast[pointer](d +% n.offset), cast[pointer](s +% n.offset), - n.typ.size) - if m != nil: - genericDeepCopyAux(dest, src, m) - of nkNone: sysAssert(false, "genericDeepCopyAux") - - proc copyDeepString(dr: var MemRegion; stack: var PointerStackChunk; src: NimString): NimString {.inline.} = - result = rawNewStringNoInit(dr, src.len) - result.len = src.len - copyMem(result.data, src.data, src.len + 1) - - proc genericDeepCopyAux(dr: var MemRegion; stack: var PointerStackChunk; - dest, src: pointer, mt: PNimType) = - var - d = cast[ByteAddress](dest) - s = cast[ByteAddress](src) - sysAssert(mt != nil, "genericDeepCopyAux 2") - case mt.kind - of tyString: - var x = cast[PPointer](dest) - var s2 = cast[PPointer](s)[] - if s2 == nil: - x[] = nil - else: - x[] = copyDeepString(cast[NimString](s2)) - of tySequence: - var s2 = cast[PPointer](src)[] - var seq = cast[PGenericSeq](s2) - var x = cast[PPointer](dest) - if s2 == nil: - x[] = nil - return - sysAssert(dest != nil, "genericDeepCopyAux 3") - x[] = newSeq(mt, seq.len) - var dst = cast[ByteAddress](cast[PPointer](dest)[]) - for i in 0..seq.len-1: - genericDeepCopyAux(dr, stack, - cast[pointer](dst +% i*% mt.base.size +% GenericSeqSize), - cast[pointer](cast[ByteAddress](s2) +% i *% mt.base.size +% - GenericSeqSize), - mt.base) - of tyObject: - # we need to copy m_type field for tyObject, as it could be empty for - # sequence reallocations: - var pint = cast[ptr PNimType](dest) - pint[] = cast[ptr PNimType](src)[] - if mt.base != nil: - genericDeepCopyAux(dr, stack, dest, src, mt.base) - genericDeepCopyAux(dr, stack, dest, src, mt.node) - of tyTuple: - genericDeepCopyAux(dr, stack, dest, src, mt.node) - of tyArray, tyArrayConstr: - for i in 0..(mt.size div mt.base.size)-1: - genericDeepCopyAux(dr, stack, - cast[pointer](d +% i*% mt.base.size), - cast[pointer](s +% i*% mt.base.size), mt.base) - of tyRef: - let s2 = cast[PPointer](src)[] - if s2 == nil: - cast[PPointer](dest)[] = nil - else: - # we modify the header of the cell temporarily; instead of the type - # field we store a forwarding pointer. XXX This is bad when the cloning - # fails due to OOM etc. - let x = usrToCell(s2) - let forw = cast[int](x.typ) - if (forw and 1) == 1: - # we stored a forwarding pointer, so let's use that: - let z = cast[pointer](forw and not 1) - unsureAsgnRef(cast[PPointer](dest), z) - else: - let realType = x.typ - let z = newObj(realType, realType.base.size) - - unsureAsgnRef(cast[PPointer](dest), z) - x.typ = cast[PNimType](cast[int](z) or 1) - genericDeepCopyAux(dr, stack, z, s2, realType.base) - x.typ = realType - else: - copyMem(dest, src, mt.size) - - proc joinAliveDataFromRegion*(dest: var MemRegion; src: var MemRegion; - root: pointer): pointer = - # we mark the alive data and copy only alive data over to 'dest'. - # This is O(liveset) but it nicely compacts memory, so it's fine. - # We use the 'typ' field as a forwarding pointer. The forwarding - # pointers have bit 0 set, so we can disambiguate them. - # We allocate a temporary stack in 'src' that we later free: - var s: PointerStackChunk - s.len = 1 - s.data[0] = root - while s.len > 0: - var p: pointer - if s.tail == nil: - p = s.data[s.len-1] - dec s.len - else: - p = s.tail.data[s.tail.len-1] - dec s.tail.len - if s.tail.len == 0: - unlink(s, s.tail) - proc rawNewObj(r: var MemRegion, typ: PNimType, size: int): pointer = var res = cast[ptr ObjHeader](alloc(r, size + sizeof(ObjHeader))) res.typ = typ @@ -374,6 +277,12 @@ proc rawNewObj(r: var MemRegion, typ: PNimType, size: int): pointer = r.head.head = res result = res +! sizeof(ObjHeader) +proc rawNewSeq(r: var MemRegion, typ: PNimType, size: int): pointer = + var res = cast[ptr SeqHeader](alloc(r, size + sizeof(SeqHeader))) + res.typ = typ + res.region = addr(r) + result = res +! sizeof(SeqHeader) + proc newObj(typ: PNimType, size: int): pointer {.compilerRtl.} = result = rawNewObj(tlRegion, typ, size) zeroMem(result, size) @@ -384,28 +293,37 @@ proc newObjNoInit(typ: PNimType, size: int): pointer {.compilerRtl.} = when defined(memProfiler): nimProfile(size) proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} = - let size = addInt(mulInt(len, typ.base.size), GenericSeqSize) - result = newObj(typ, size) + let size = roundup(addInt(mulInt(len, typ.base.size), GenericSeqSize), + MemAlign) + result = rawNewSeq(tlRegion, typ, size) + zeroMem(result, size) cast[PGenericSeq](result).len = len cast[PGenericSeq](result).reserved = len +proc newStr(typ: PNimType, len: int; init: bool): pointer {.compilerRtl.} = + let size = roundup(addInt(len, GenericSeqSize), MemAlign) + result = rawNewSeq(tlRegion, typ, size) + if init: zeroMem(result, size) + cast[PGenericSeq](result).len = 0 + cast[PGenericSeq](result).reserved = len + proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} = result = rawNewObj(tlRegion, typ, size) zeroMem(result, size) proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.} = - let size = addInt(mulInt(len, typ.base.size), GenericSeqSize) - result = newObj(typ, size) - cast[PGenericSeq](result).len = len - cast[PGenericSeq](result).reserved = len + result = newSeq(typ, len) -proc growObj(region: var MemRegion; old: pointer, newsize: int): pointer = - let typ = cast[ptr ObjHeader](old -! sizeof(ObjHeader)).typ - result = rawNewObj(region, typ, newsize) +proc growObj(regionUnused: var MemRegion; old: pointer, newsize: int): pointer = + let sh = cast[ptr SeqHeader](old -! sizeof(SeqHeader)) + let typ = sh.typ + result = rawNewSeq(sh.region[], typ, + roundup(newsize, MemAlign)) let elemSize = if typ.kind == tyString: 1 else: typ.base.size let oldsize = cast[PGenericSeq](old).len*elemSize + GenericSeqSize - copyMem(result, old, oldsize) zeroMem(result +! oldsize, newsize-oldsize) + copyMem(result, old, oldsize) + dealloc(sh.region[], old, roundup(oldsize, MemAlign)) proc growObj(old: pointer, newsize: int): pointer {.rtl.} = result = growObj(tlRegion, old, newsize) diff --git a/lib/system/jssys.nim b/lib/system/jssys.nim index 768f9bc17..f23be2d78 100644 --- a/lib/system/jssys.nim +++ b/lib/system/jssys.nim @@ -53,7 +53,7 @@ proc isNimException(): bool {.asmNoStackFrame.} = else: asm "return `lastJSError`.m_type;" -proc getCurrentException*(): ref Exception = +proc getCurrentException*(): ref Exception {.compilerRtl, benign.} = if isNimException(): result = cast[ref Exception](lastJSError) proc getCurrentExceptionMsg*(): string = @@ -157,10 +157,10 @@ proc reraiseException() {.compilerproc, asmNoStackFrame.} = asm "throw lastJSError;" -proc raiseOverflow {.exportc: "raiseOverflow", noreturn.} = +proc raiseOverflow {.exportc: "raiseOverflow", noreturn, compilerProc.} = raise newException(OverflowError, "over- or underflow") -proc raiseDivByZero {.exportc: "raiseDivByZero", noreturn.} = +proc raiseDivByZero {.exportc: "raiseDivByZero", noreturn, compilerProc.} = raise newException(DivByZeroError, "division by zero") proc raiseRangeError() {.compilerproc, noreturn.} = diff --git a/lib/system/mmdisp.nim b/lib/system/mmdisp.nim index 5b5ba9490..d2160fdac 100644 --- a/lib/system/mmdisp.nim +++ b/lib/system/mmdisp.nim @@ -16,7 +16,7 @@ const debugGC = false # we wish to debug the GC... logGC = false - traceGC = defined(smokeCycles) # extensive debugging + traceGC = false # extensive debugging alwaysCycleGC = defined(smokeCycles) alwaysGC = defined(fulldebug) # collect after every memory # allocation (for debugging) @@ -34,7 +34,7 @@ const type PPointer = ptr pointer - ByteArray = array[0..ArrayDummySize, byte] + ByteArray = UncheckedArray[byte] PByte = ptr ByteArray PString = ptr string {.deprecated: [TByteArray: ByteArray].} @@ -543,7 +543,7 @@ elif defined(nogc): include "system/cellsets" else: - when not defined(gcStack): + when not defined(gcRegions): include "system/alloc" include "system/cellsets" @@ -551,9 +551,9 @@ else: sysAssert(sizeof(Cell) == sizeof(FreeCell), "sizeof FreeCell") when compileOption("gc", "v2"): include "system/gc2" - elif defined(gcStack): + elif defined(gcRegions): # XXX due to bootstrapping reasons, we cannot use compileOption("gc", "stack") here - include "system/gc_stack" + include "system/gc_regions" elif defined(gcMarkAndSweep): # XXX use 'compileOption' here include "system/gc_ms" diff --git a/lib/system/platforms.nim b/lib/system/platforms.nim index eeada5c51..8939615cd 100644 --- a/lib/system/platforms.nim +++ b/lib/system/platforms.nim @@ -24,6 +24,8 @@ type amd64, ## x86_64 (AMD64); 64 bit x86 compatible CPU mips, ## Mips based processor mipsel, ## Little Endian Mips based processor + mips64, ## 64-bit MIPS processor + mips64el, ## Little Endian 64-bit MIPS processor arm, ## ARM based processor arm64, ## ARM64 based processor vm, ## Some Virtual machine: Nim's VM or JavaScript @@ -33,7 +35,8 @@ type OsPlatform* {.pure.} = enum ## the OS this program will run on. none, dos, windows, os2, linux, morphos, skyos, solaris, irix, netbsd, freebsd, openbsd, aix, palmos, qnx, amiga, - atari, netware, macos, macosx, haiku, js, nimVM, standalone + atari, netware, macos, macosx, haiku, android, js, nimVM, + standalone const targetOS* = when defined(windows): OsPlatform.windows @@ -56,6 +59,7 @@ const elif defined(macosx): OsPlatform.macosx elif defined(macos): OsPlatform.macos elif defined(haiku): OsPlatform.haiku + elif defined(android): OsPlatform.android elif defined(js): OsPlatform.js elif defined(nimrodVM): OsPlatform.nimVM elif defined(standalone): OsPlatform.standalone @@ -73,6 +77,8 @@ const elif defined(amd64): CpuPlatform.amd64 elif defined(mips): CpuPlatform.mips elif defined(mipsel): CpuPlatform.mipsel + elif defined(mips64): CpuPlatform.mips64 + elif defined(mips64el): CpuPlatform.mips64el elif defined(arm): CpuPlatform.arm elif defined(arm64): CpuPlatform.arm64 elif defined(vm): CpuPlatform.vm diff --git a/lib/system/sysio.nim b/lib/system/sysio.nim index 7444661e3..7b6d93fe0 100644 --- a/lib/system/sysio.nim +++ b/lib/system/sysio.nim @@ -86,11 +86,11 @@ proc writeBuffer(f: File, buffer: pointer, len: Natural): int = checkErr(f) proc writeBytes(f: File, a: openArray[int8|uint8], start, len: Natural): int = - var x = cast[ptr array[0..1000_000_000, int8]](a) - result = writeBuffer(f, addr(x[start]), len) + var x = cast[ptr UncheckedArray[int8]](a) + result = writeBuffer(f, addr(x[int(start)]), len) proc writeChars(f: File, a: openArray[char], start, len: Natural): int = - var x = cast[ptr array[0..1000_000_000, int8]](a) - result = writeBuffer(f, addr(x[start]), len) + var x = cast[ptr UncheckedArray[int8]](a) + result = writeBuffer(f, addr(x[int(start)]), len) proc write(f: File, s: string) = if writeBuffer(f, cstring(s), s.len) != s.len: diff --git a/lib/system/sysstr.nim b/lib/system/sysstr.nim index d3d3d5a95..c3150cb07 100644 --- a/lib/system/sysstr.nim +++ b/lib/system/sysstr.nim @@ -38,6 +38,13 @@ when declared(allocAtomic): template allocStrNoInit(size: untyped): untyped = cast[NimString](boehmAllocAtomic(size)) +elif defined(gcRegions): + template allocStr(size: untyped): untyped = + cast[NimString](newStr(addr(strDesc), size, true)) + + template allocStrNoInit(size: untyped): untyped = + cast[NimString](newStr(addr(strDesc), size, false)) + else: template allocStr(size: untyped): untyped = cast[NimString](newObj(addr(strDesc), size)) @@ -99,7 +106,7 @@ proc copyString(src: NimString): NimString {.compilerRtl.} = proc copyStringRC1(src: NimString): NimString {.compilerRtl.} = if src != nil: - when declared(newObjRC1): + when declared(newObjRC1) and not defined(gcRegions): var s = src.len if s < 7: s = 7 result = cast[NimString](newObjRC1(addr(strDesc), sizeof(TGenericSeq) + @@ -235,7 +242,7 @@ proc setLengthSeq(seq: PGenericSeq, elemSize, newLen: int): PGenericSeq {. # we need to decref here, otherwise the GC leaks! when not defined(boehmGC) and not defined(nogc) and not defined(gcMarkAndSweep) and not defined(gogc) and - not defined(gcStack): + not defined(gcRegions): when false: # compileOption("gc", "v2"): for i in newLen..result.len-1: let len0 = gch.tempStack.len diff --git a/lib/system/threads.nim b/lib/system/threads.nim index 49b13576c..a7a811844 100644 --- a/lib/system/threads.nim +++ b/lib/system/threads.nim @@ -115,10 +115,6 @@ when defined(windows): proc setThreadAffinityMask(hThread: SysThread, dwThreadAffinityMask: uint) {. importc: "SetThreadAffinityMask", stdcall, header: "<windows.h>".} - proc getThreadId*(): int = - ## get the ID of the currently running thread. - result = int(getCurrentThreadId()) - elif defined(genode): const GenodeHeader = "genode_cpp/threads.h" @@ -249,48 +245,6 @@ else: proc setAffinity(thread: SysThread; setsize: csize; s: var CpuSet) {. importc: "pthread_setaffinity_np", header: pthreadh.} - when defined(linux): - proc syscall(arg: clong): clong {.varargs, importc: "syscall", header: "<unistd.h>".} - var NR_gettid {.importc: "__NR_gettid", header: "<sys/syscall.h>".}: int - - #type Pid {.importc: "pid_t", header: "<sys/types.h>".} = distinct int - #proc gettid(): Pid {.importc, header: "<sys/types.h>".} - - proc getThreadId*(): int = - ## get the ID of the currently running thread. - result = int(syscall(NR_gettid)) - elif defined(dragonfly): - proc lwp_gettid(): int32 {.importc, header: "unistd.h".} - - proc getThreadId*(): int = - result = int(lwp_gettid()) - elif defined(openbsd): - proc getthrid(): int32 {.importc: "getthrid", header: "<unistd.h>".} - - proc getThreadId*(): int = - result = int(getthrid()) - elif defined(netbsd): - proc lwp_self(): int32 {.importc: "_lwp_self", header: "<lwp.h>".} - - proc getThreadId*(): int = - result = int(lwp_self()) - elif defined(macosx) or defined(freebsd): - proc pthread_threadid_np(y: pointer; x: var uint64): cint {.importc, header: "pthread.h".} - - proc getThreadId*(): int = - ## get the ID of the currently running thread. - var x: uint64 - result = pthread_threadid_np(nil, x) - result = int(x) - elif defined(solaris): - # just a guess really: - type thread_t {.importc: "thread_t", header: "<thread.h>".} = distinct int - proc thr_self(): thread_t {.importc, header: "<thread.h>".} - - proc getThreadId*(): int = - ## get the ID of the currently running thread. - result = int(thr_self()) - const emulatedThreadVars = compileOption("tlsEmulation") @@ -302,8 +256,9 @@ when emulatedThreadVars: # we preallocate a fixed size for thread local storage, so that no heap # allocations are needed. Currently less than 7K are used on a 64bit machine. # We use ``float`` for proper alignment: +const nimTlsSize {.intdefine.} = 8000 type - ThreadLocalStorage = array[0..1_000, float] + ThreadLocalStorage = array[0..(nimTlsSize div sizeof(float)), float] PGcThread = ptr GcThread GcThread {.pure, inheritable.} = object @@ -369,7 +324,11 @@ when not defined(useNimRtl): when emulatedThreadVars: if nimThreadVarsSize() > sizeof(ThreadLocalStorage): - echo "too large thread local storage size requested" + echo "too large thread local storage size requested ", + "(", nimThreadVarsSize(), "/", sizeof(ThreadLocalStorage), "). ", + "Use -d:\"nimTlsSize=", nimThreadVarsSize(), + "\" to preallocate sufficient storage." + quit 1 when hasSharedHeap and not defined(boehmgc) and not defined(gogc) and not defined(nogc): @@ -437,7 +396,7 @@ template afterThreadRuns() = for i in countdown(threadDestructionHandlers.len-1, 0): threadDestructionHandlers[i]() -when not defined(boehmgc) and not hasSharedHeap and not defined(gogc) and not defined(gcstack): +when not defined(boehmgc) and not hasSharedHeap and not defined(gogc) and not defined(gcRegions): proc deallocOsPages() when defined(boehmgc): @@ -475,7 +434,7 @@ else: proc threadProcWrapStackFrame[TArg](thrd: ptr Thread[TArg]) = when defined(boehmgc): boehmGC_call_with_stack_base(threadProcWrapDispatch[TArg], thrd) - elif not defined(nogc) and not defined(gogc) and not defined(gcstack): + elif not defined(nogc) and not defined(gogc) and not defined(gcRegions): var p {.volatile.}: proc(a: ptr Thread[TArg]) {.nimcall.} = threadProcWrapDispatch[TArg] when not hasSharedHeap: @@ -493,7 +452,7 @@ proc threadProcWrapStackFrame[TArg](thrd: ptr Thread[TArg]) = else: threadProcWrapDispatch(thrd) -template threadProcWrapperBody(closure: expr) {.immediate.} = +template threadProcWrapperBody(closure: untyped): untyped = var thrd = cast[ptr Thread[TArg]](closure) var core = thrd.core when declared(globalsSlot): threadVarSetValue(globalsSlot, thrd.core) @@ -665,3 +624,82 @@ when useStackMaskHack: var mainThread: Thread[pointer] createThread(mainThread, tp) joinThread(mainThread) + +## we need to cache current threadId to not perform syscall all the time +var threadId {.threadvar.}: int + +when defined(windows): + proc getThreadId*(): int = + ## get the ID of the currently running thread. + if threadId == 0: + threadId = int(getCurrentThreadId()) + result = threadId + +elif defined(linux): + proc syscall(arg: clong): clong {.varargs, importc: "syscall", header: "<unistd.h>".} + var NR_gettid {.importc: "__NR_gettid", header: "<sys/syscall.h>".}: int + + proc getThreadId*(): int = + ## get the ID of the currently running thread. + if threadId == 0: + threadId = int(syscall(NR_gettid)) + result = threadId + +elif defined(dragonfly): + proc lwp_gettid(): int32 {.importc, header: "unistd.h".} + + proc getThreadId*(): int = + ## get the ID of the currently running thread. + if threadId == 0: + threadId = int(lwp_gettid()) + result = threadId + +elif defined(openbsd): + proc getthrid(): int32 {.importc: "getthrid", header: "<unistd.h>".} + + proc getThreadId*(): int = + ## get the ID of the currently running thread. + if threadId == 0: + threadId = int(getthrid()) + result = threadId + +elif defined(netbsd): + proc lwp_self(): int32 {.importc: "_lwp_self", header: "<lwp.h>".} + + proc getThreadId*(): int = + ## get the ID of the currently running thread. + if threadId == 0: + threadId = int(lwp_self()) + result = threadId + +elif defined(freebsd): + proc syscall(arg: cint, arg0: ptr cint): cint {.varargs, importc: "syscall", header: "<unistd.h>".} + var SYS_thr_self {.importc:"SYS_thr_self", header:"<sys/syscall.h>"}: cint + + proc getThreadId*(): int = + ## get the ID of the currently running thread. + var tid = 0.cint + if threadId == 0: + discard syscall(SYS_thr_self, addr tid) + threadId = tid + result = threadId + +elif defined(macosx): + proc syscall(arg: cint): cint {.varargs, importc: "syscall", header: "<unistd.h>".} + var SYS_thread_selfid {.importc:"SYS_thread_selfid", header:"<sys/syscall.h>".}: cint + + proc getThreadId*(): int = + ## get the ID of the currently running thread. + if threadId == 0: + threadId = int(syscall(SYS_thread_selfid)) + result = threadId + +elif defined(solaris): + type thread_t {.importc: "thread_t", header: "<thread.h>".} = distinct int + proc thr_self(): thread_t {.importc, header: "<thread.h>".} + + proc getThreadId*(): int = + ## get the ID of the currently running thread. + if threadId == 0: + threadId = int(thr_self()) + result = threadId diff --git a/lib/system/widestrs.nim b/lib/system/widestrs.nim index dda547abe..a8b28c279 100644 --- a/lib/system/widestrs.nim +++ b/lib/system/widestrs.nim @@ -15,7 +15,7 @@ when not declared(NimString): type Utf16Char* = distinct int16 - WideCString* = ref array[0.. 1_000_000, Utf16Char] + WideCString* = ref UncheckedArray[Utf16Char] {.deprecated: [TUtf16Char: Utf16Char].} proc len*(w: WideCString): int = diff --git a/lib/upcoming/asyncdispatch.nim b/lib/upcoming/asyncdispatch.nim index 1623d8375..4e3b06173 100644 --- a/lib/upcoming/asyncdispatch.nim +++ b/lib/upcoming/asyncdispatch.nim @@ -9,11 +9,13 @@ include "system/inclrtl" -import os, tables, strutils, times, heapqueue, lists, options +import os, tables, strutils, times, heapqueue, lists, options, asyncstreams +import asyncfutures except callSoon import nativesockets, net, deques export Port, SocketFlag +export asyncfutures, asyncstreams #{.injectStmt: newGcInvariant().} @@ -130,8 +132,6 @@ export Port, SocketFlag # TODO: Check if yielded future is nil and throw a more meaningful exception -include "../includes/asyncfutures" - type PDispatcherBase = ref object of RootRef timers: HeapQueue[tuple[finishAt: float, fut: Future[void]]] @@ -161,6 +161,12 @@ proc adjustedTimeout(p: PDispatcherBase, timeout: int): int {.inline.} = result = int((timerTimeout - curTime) * 1000) if result < 0: result = 0 +proc callSoon(cbproc: proc ()) {.gcsafe.} + +proc initCallSoonProc = + if asyncfutures.getCallSoonProc().isNil: + asyncfutures.setCallSoonProc(callSoon) + when defined(windows) or defined(nimdoc): import winlean, sets, hashes type @@ -214,15 +220,17 @@ when defined(windows) or defined(nimdoc): result.callbacks = initDeque[proc ()](64) var gDisp{.threadvar.}: PDispatcher ## Global dispatcher - proc getGlobalDispatcher*(): PDispatcher = - ## Retrieves the global thread-local dispatcher. - if gDisp.isNil: gDisp = newDispatcher() - result = gDisp proc setGlobalDispatcher*(disp: PDispatcher) = if not gDisp.isNil: assert gDisp.callbacks.len == 0 gDisp = disp + initCallSoonProc() + + proc getGlobalDispatcher*(): PDispatcher = + if gDisp.isNil: + setGlobalDispatcher(newDispatcher()) + result = gDisp proc register*(fd: AsyncFD) = ## Registers ``fd`` with the dispatcher. @@ -1081,14 +1089,17 @@ else: result.callbacks = initDeque[proc ()](InitDelayedCallbackListSize) var gDisp{.threadvar.}: PDispatcher ## Global dispatcher - proc getGlobalDispatcher*(): PDispatcher = - if gDisp.isNil: gDisp = newDispatcher() - result = gDisp proc setGlobalDispatcher*(disp: PDispatcher) = if not gDisp.isNil: assert gDisp.callbacks.len == 0 gDisp = disp + initCallSoonProc() + + proc getGlobalDispatcher*(): PDispatcher = + if gDisp.isNil: + setGlobalDispatcher(newDispatcher()) + result = gDisp proc register*(fd: AsyncFD) = let p = getGlobalDispatcher() @@ -1601,7 +1612,7 @@ proc recvLine*(socket: AsyncFD): Future[string] {.async.} = return add(result, c) -proc callSoon*(cbproc: proc ()) = +proc callSoon(cbproc: proc ()) = ## Schedule `cbproc` to be called as soon as possible. ## The callback is called when control returns to the event loop. getGlobalDispatcher().callbacks.addLast(cbproc) diff --git a/lib/wrappers/openssl.nim b/lib/wrappers/openssl.nim index ff18fc2c2..40b54b08d 100644 --- a/lib/wrappers/openssl.nim +++ b/lib/wrappers/openssl.nim @@ -588,13 +588,13 @@ proc md5*(d: ptr cuchar; n: csize; md: ptr cuchar): ptr cuchar{.importc: "MD5".} proc md5_Transform*(c: var MD5_CTX; b: ptr cuchar){.importc: "MD5_Transform".} {.pop.} -from strutils import toHex,toLower +from strutils import toHex, toLowerAscii proc hexStr (buf:cstring): string = # turn md5s output into a nice hex str result = newStringOfCap(32) for i in 0 .. <16: - result.add toHex(buf[i].ord, 2).toLower + result.add toHex(buf[i].ord, 2).toLowerAscii proc md5_File* (file: string): string {.raises: [IOError,Exception].} = ## Generate MD5 hash for a file. Result is a 32 character diff --git a/readme.md b/readme.md index 5e50bbc41..44570880a 100644 --- a/readme.md +++ b/readme.md @@ -84,8 +84,14 @@ within the [doc/koch.rst](doc/koch.rst) file. ``bin`` directory to your PATH, you may install Nimble from source by running ``koch nimble`` within the root of the cloned repository. + +## Contributors + +This project exists thanks to all the people who contribute. [Read on to find out how to contribute](#contributing). +<a href="https://github.com/nim-lang/Nim/graphs/contributors"><img src="https://opencollective.com/Nim/contributors.svg?width=890" /></a> + ## Contributing -[![Contribute to Nim via Gratipay][badge-nim-gratipay]][nim-gratipay] +[](#backers) [](#sponsors) [![Contribute to Nim via Gratipay][badge-nim-gratipay]][nim-gratipay] [![Setup a bounty via Bountysource][badge-nim-bountysource]][nim-bountysource] [![Donate Bitcoins][badge-nim-bitcoin]][nim-bitcoin] @@ -128,6 +134,7 @@ be a good starting point for an initial contribution to Nim. You can also help with the development of Nim by making donations. Donations can be made using: +* [Open Collective](https://opencollective.com/nim) * [Gratipay][nim-gratipay] * [Bountysource][nim-bountysource] * [Bitcoin][nim-bitcoin] @@ -135,6 +142,31 @@ made using: If you have any questions feel free to submit a question on the [Nim forum][nim-forum], or via IRC on [the \#nim channel][nim-irc]. + +## Backers + +Thank you to all our backers! [[Become a backer](https://opencollective.com/Nim#backer)] + +<a href="https://opencollective.com/Nim#backers" target="_blank"><img src="https://opencollective.com/Nim/backers.svg?width=890"></a> + + +## Sponsors + +Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/Nim#sponsor)] + +<a href="https://opencollective.com/Nim/sponsor/0/website" target="_blank"><img src="https://opencollective.com/Nim/sponsor/0/avatar.svg"></a> +<a href="https://opencollective.com/Nim/sponsor/1/website" target="_blank"><img src="https://opencollective.com/Nim/sponsor/1/avatar.svg"></a> +<a href="https://opencollective.com/Nim/sponsor/2/website" target="_blank"><img src="https://opencollective.com/Nim/sponsor/2/avatar.svg"></a> +<a href="https://opencollective.com/Nim/sponsor/3/website" target="_blank"><img src="https://opencollective.com/Nim/sponsor/3/avatar.svg"></a> +<a href="https://opencollective.com/Nim/sponsor/4/website" target="_blank"><img src="https://opencollective.com/Nim/sponsor/4/avatar.svg"></a> +<a href="https://opencollective.com/Nim/sponsor/5/website" target="_blank"><img src="https://opencollective.com/Nim/sponsor/5/avatar.svg"></a> +<a href="https://opencollective.com/Nim/sponsor/6/website" target="_blank"><img src="https://opencollective.com/Nim/sponsor/6/avatar.svg"></a> +<a href="https://opencollective.com/Nim/sponsor/7/website" target="_blank"><img src="https://opencollective.com/Nim/sponsor/7/avatar.svg"></a> +<a href="https://opencollective.com/Nim/sponsor/8/website" target="_blank"><img src="https://opencollective.com/Nim/sponsor/8/avatar.svg"></a> +<a href="https://opencollective.com/Nim/sponsor/9/website" target="_blank"><img src="https://opencollective.com/Nim/sponsor/9/avatar.svg"></a> + +You can also see a list of all our sponsors/backers from various payment services on the [sponsors page](https://nim-lang.org/sponsors.html) of our website. + ## License The compiler and the standard library are licensed under the MIT license, except for some modules which explicitly state otherwise. As a result you may use any diff --git a/tests/alias/talias.nim b/tests/alias/talias.nim index 810ea2095..a8f1b0dd0 100644 --- a/tests/alias/talias.nim +++ b/tests/alias/talias.nim @@ -8,17 +8,17 @@ proc isPartOf*[S, T](a: S, b: T): TAnalysisResult {. magic: "IsPartOf", noSideEffect.} ## not yet exported properly. -template compileTimeAssert(cond: expr) = +template compileTimeAssert(cond) = when not cond: {.compile: "is false: " & astToStr(cond).} -template `<|` (a, b: expr) = +template `<|` (a, b) = compileTimeAssert isPartOf(a, b) == arYes -template `!<|` (a, b: expr) = +template `!<|` (a, b) = compileTimeAssert isPartOf(a, b) == arNo -template `?<|` (a, b: expr) = +template `?<|` (a, b) = compileTimeAssert isPartOf(a, b) == arMaybe type diff --git a/tests/arithm/tshr.nim b/tests/arithm/tshr.nim index e9b72f1df..4ba34aed9 100644 --- a/tests/arithm/tshr.nim +++ b/tests/arithm/tshr.nim @@ -3,18 +3,18 @@ discard """ """ proc T() = - let VI = -8 - let VI64 = -8'i64 - let VI32 = -8'i32 - let VI16 = -8'i16 - let VI8 = -8'i8 - doAssert( (VI shr 1) == 9223372036854775804) - doAssert( (VI64 shr 1) == 9223372036854775804) - doAssert( (VI32 shr 1) == 2147483644) - doAssert( (VI16 shr 1) == 32764) - doAssert( (VI8 shr 1) == 124) + # let VI = -8 + let VI64 = -8'i64 + let VI32 = -8'i32 + let VI16 = -8'i16 + let VI8 = -8'i8 + # doAssert( (VI shr 1) == 9_223_372_036_854_775_804, "Actual: " & $(VI shr 1)) + doAssert( (VI64 shr 1) == 9_223_372_036_854_775_804, "Actual: " & $(VI64 shr 1)) + doAssert( (VI32 shr 1) == 2_147_483_644, "Actual: " & $(VI32 shr 1)) + doAssert( (VI16 shr 1) == 32_764, "Actual: " & $(VI16 shr 1)) + doAssert( (VI8 shr 1) == 124, "Actual: " & $(VI8 shr 1)) T() static: - T() + T() diff --git a/tests/array/tunchecked.nim b/tests/array/tunchecked.nim new file mode 100644 index 000000000..f5ac3642d --- /dev/null +++ b/tests/array/tunchecked.nim @@ -0,0 +1,5 @@ +{.boundchecks: on.} +type Unchecked {.unchecked.} = array[0, char] + +var x = cast[ptr Unchecked](alloc(100)) +x[5] = 'x' diff --git a/tests/assert/tuserassert.nim b/tests/assert/tuserassert.nim index 7bb0a7fc0..274c78921 100644 --- a/tests/assert/tuserassert.nim +++ b/tests/assert/tuserassert.nim @@ -2,7 +2,7 @@ discard """ output: "x == 45ugh" """ -template myAssert(cond: expr) = +template myAssert(cond: untyped) = when 3 <= 3: let c = cond.astToStr if not cond: diff --git a/tests/async/tasync_gcsafe.nim b/tests/async/tasync_gcsafe.nim new file mode 100644 index 000000000..89df6456a --- /dev/null +++ b/tests/async/tasync_gcsafe.nim @@ -0,0 +1,36 @@ +discard """ + cmd: "nim c --threads:on $file" + output: ''' +1 +2 +3 +''' +""" + +assert compileOption("threads"), "this test will not do anything useful without --threads:on" + +import asyncdispatch + +var globalDummy: ref int +proc gcUnsafeProc() = + if not globalDummy.isNil: + echo globalDummy[] + echo "1" + +proc gcSafeAsyncProcWithNoAnnotation() {.async.} = + echo "2" + +proc gcSafeAsyncProcWithAnnotation() {.gcsafe, async.} = + echo "3" + +proc gcUnsafeAsyncProc() {.async.} = + # We should be able to call gcUnsafe + gcUnsafeProc() + + # We should be able to call async implicitly gcsafe + await gcSafeAsyncProcWithNoAnnotation() + + # We should be able to call async explicitly gcsafe + await gcSafeAsyncProcWithAnnotation() + +waitFor gcUnsafeAsyncProc() diff --git a/tests/async/tasync_gcunsafe.nim b/tests/async/tasync_gcunsafe.nim new file mode 100644 index 000000000..f4e2cdcf2 --- /dev/null +++ b/tests/async/tasync_gcunsafe.nim @@ -0,0 +1,30 @@ +discard """ + cmd: "nim c --threads:on $file" + file: "asyncmacro.nim" + errormsg: "'anotherGCSafeAsyncProcIter' is not GC-safe as it calls 'asyncGCUnsafeProc'" +""" + +assert compileOption("threads"), "this test will not do anything useful without --threads:on" + +import asyncdispatch + +var globalDummy: ref int +proc gcUnsafeProc() = + if not globalDummy.isNil: + echo globalDummy[] + +proc asyncExplicitlyGCSafeProc() {.gcsafe, async.} = + echo "hi" + +proc asyncImplicitlyGCSafeProc() {.async.} = + echo "hi" + +proc asyncGCUnsafeProc() {.async.} = + gcUnsafeProc() + +proc anotherGCSafeAsyncProc() {.async, gcsafe.} = + # We should be able to call other gcsafe procs + await asyncExplicitlyGCSafeProc() + await asyncImplicitlyGCSafeProc() + # But we can't call gcunsafe procs + await asyncGCUnsafeProc() diff --git a/tests/async/tasyncrecursion.nim b/tests/async/tasyncrecursion.nim index 54482edab..1aeebe9b4 100644 --- a/tests/async/tasyncrecursion.nim +++ b/tests/async/tasyncrecursion.nim @@ -17,5 +17,6 @@ proc asyncRecursionTest*(): Future[int] {.async.} = inc(i) when isMainModule: + setGlobalDispatcher(newDispatcher()) var i = waitFor asyncRecursionTest() echo i diff --git a/tests/async/tcallbacks.nim b/tests/async/tcallbacks.nim new file mode 100644 index 000000000..8c08032cd --- /dev/null +++ b/tests/async/tcallbacks.nim @@ -0,0 +1,20 @@ +discard """ + exitcode: 0 + output: '''3 +2 +1 +5 +''' +""" +import asyncfutures + +let f1: Future[int] = newFuture[int]() +f1.addCallback(proc() = echo 1) +f1.addCallback(proc() = echo 2) +f1.addCallback(proc() = echo 3) +f1.complete(10) + +let f2: Future[int] = newFuture[int]() +f2.addCallback(proc() = echo 4) +f2.callback = proc() = echo 5 +f2.complete(10) diff --git a/tests/bind/mbind3.nim b/tests/bind/mbind3.nim index aad080223..1a7d3b63b 100644 --- a/tests/bind/mbind3.nim +++ b/tests/bind/mbind3.nim @@ -2,7 +2,7 @@ var lastId = 0 -template genId*: expr = +template genId*: int = bind lastId inc(lastId) lastId diff --git a/tests/bind/tbind1.nim b/tests/bind/tbind1.nim index 2665d0b5c..9b13a7d11 100644 --- a/tests/bind/tbind1.nim +++ b/tests/bind/tbind1.nim @@ -6,7 +6,7 @@ discard """ proc p1(x: int8, y: int): int = return x + y -template tempBind(x, y: expr): expr = +template tempBind(x, y): untyped = bind p1 p1(x, y) diff --git a/tests/bind/tbind2.nim b/tests/bind/tbind2.nim index 0e0cbd788..799b14381 100644 --- a/tests/bind/tbind2.nim +++ b/tests/bind/tbind2.nim @@ -8,7 +8,7 @@ discard """ proc p1(x: int8, y: int): int = return x + y proc p1(x: int, y: int8): int = return x - y -template tempBind(x, y: expr): expr = +template tempBind(x, y): untyped = (bind p1(x, y)) #ERROR_MSG ambiguous call echo tempBind(1'i8, 2'i8) diff --git a/tests/ccgbugs/t6279.nim b/tests/ccgbugs/t6279.nim new file mode 100644 index 000000000..37345d807 --- /dev/null +++ b/tests/ccgbugs/t6279.nim @@ -0,0 +1,9 @@ +discard """ +cmd: "nim c -r -d:fulldebug -d:smokeCycles --gc:refc $file" +output: '''@[a]''' +""" + +# bug #6279 +var foo = newSeq[tuple[a: seq[string], b: seq[string]]]() +foo.add((@["a"], @["b"])) +echo foo[0].a # Crashes on this line diff --git a/tests/ccgbugs/taddhigh.nim b/tests/ccgbugs/taddhigh.nim index d6ac8f650..549eb8caa 100644 --- a/tests/ccgbugs/taddhigh.nim +++ b/tests/ccgbugs/taddhigh.nim @@ -1,5 +1,5 @@ discard """ - output: '''@[5, 5, 5]''' + output: '''@[5, 5, 5, 5, 5]''' """ # bug #1832 @@ -13,4 +13,7 @@ s.add x # Causes the 0 to appear: s.add s[s.high] +s.add s[s.len-1] +s.add s[s.xlen-1] + echo s # @[5, 5, 0] diff --git a/tests/ccgbugs/tnocodegen_for_compiletime.nim b/tests/ccgbugs/tnocodegen_for_compiletime.nim index a88ba4b32..b44e9f8c9 100644 --- a/tests/ccgbugs/tnocodegen_for_compiletime.nim +++ b/tests/ccgbugs/tnocodegen_for_compiletime.nim @@ -1,7 +1,7 @@ # bug #1679 import macros, tables, hashes proc hash(v: NimNode): Hash = 4 # performance is for suckers -macro test(body: stmt): stmt {.immediate.} = +macro test(body: untyped): typed = var a = initCountTable[NimNode]() a.inc(body) diff --git a/tests/ccgbugs/tuple_canon.nim b/tests/ccgbugs/tuple_canon.nim index a607f9cab..45fed8eee 100644 --- a/tests/ccgbugs/tuple_canon.nim +++ b/tests/ccgbugs/tuple_canon.nim @@ -59,7 +59,7 @@ let ((1,1), hiA), ((0,1), hiB)] -template odd*(i: int) : expr = +template odd*(i: int) : untyped = (i and 1) != 0 proc vidx(hg: HexGrid; col, row: int; i: HexVtxIndex) : Index = diff --git a/tests/ccgbugs/twrongrefcounting.nim b/tests/ccgbugs/twrongrefcounting.nim new file mode 100644 index 000000000..c0c3e0fc2 --- /dev/null +++ b/tests/ccgbugs/twrongrefcounting.nim @@ -0,0 +1,22 @@ +discard """ + output: '''ok''' + cmd: "nim c -r --gc:refc -d:useGcAssert -d:useSysAssert -d:fulldebug -d:smokeCycles $file" +""" +# bug #6234 +type + Foo = ref object + s: seq[Bar] + Bar = ref object + f: Foo + +proc test() = + var f = Foo.new() + for i in 0 .. 5: + f.s = @[] + for j in 0 .. 5: + var b = Bar.new() + b.f = f + f.s.add(b) + +test() +echo "ok" diff --git a/tests/closure/tboehmdeepcopy.nim b/tests/closure/tboehmdeepcopy.nim new file mode 100644 index 000000000..7c937ca10 --- /dev/null +++ b/tests/closure/tboehmdeepcopy.nim @@ -0,0 +1,18 @@ +discard """ + cmd: "nim c --gc:boehm $options $file" + output: '''meep''' + disabled: "windows" +""" + +proc callit(it: proc ()) = + it() + +proc main = + var outer = "meep" + proc x = + echo outer + var y: proc() + deepCopy(y, x) + callit(y) + +main() diff --git a/tests/closure/tdonotation.nim b/tests/closure/tdonotation.nim index 94eba8ddb..cc4f46bab 100644 --- a/tests/closure/tdonotation.nim +++ b/tests/closure/tdonotation.nim @@ -5,7 +5,9 @@ lost focus 1 lost focus 2 registered handler for UserEvent 1 registered handler for UserEvent 2 -registered handler for UserEvent 3''' +registered handler for UserEvent 3 +registered handler for UserEvent 4 +''' """ import future @@ -35,11 +37,14 @@ b.onFocusLost: b.onFocusLost do: echo "lost focus 2" -b.onUserEvent "UserEvent 1" do: +b.onUserEvent("UserEvent 1") do: discard b.onUserEvent "UserEvent 2": discard -b.onUserEvent("UserEvent 3", () => echo "event 3") +b.onUserEvent("UserEvent 3"): + discard + +b.onUserEvent("UserEvent 4", () => echo "event 4") diff --git a/tests/closure/tmacrobust1512.nim b/tests/closure/tmacrobust1512.nim index 95681e750..5f13e8286 100644 --- a/tests/closure/tmacrobust1512.nim +++ b/tests/closure/tmacrobust1512.nim @@ -2,109 +2,109 @@ import macros, strutils # https://github.com/nim-lang/Nim/issues/1512 -proc macrobust0 (raw_input: string) = +proc macrobust0(raw_input: string) = var output = "" - proc p1 (a:string) = - output.add (a) - - proc p2 (a:string) = p1 (a) - proc p3 (a:string) = p2 (a) - proc p4 (a:string) = p3 (a) - proc p5 (a:string) = p4 (a) - proc p6 (a:string) = p5 (a) - proc p7 (a:string) = p6 (a) - proc p8 (a:string) = p7 (a) - proc p9 (a:string) = p8 (a) - proc p10 (a:string) = p9 (a) - proc p11 (a:string) = p10 (a) - proc p12 (a:string) = p11 (a) - proc p13 (a:string) = p12 (a) - proc p14 (a:string) = p13 (a) - proc p15 (a:string) = p14 (a) - proc p16 (a:string) = p15 (a) - proc p17 (a:string) = p16 (a) - proc p18 (a:string) = p17 (a) - proc p19 (a:string) = p18 (a) - proc p20 (a:string) = p19 (a) + proc p1(a:string) = + output.add(a) + + proc p2(a:string) = p1(a) + proc p3(a:string) = p2(a) + proc p4(a:string) = p3(a) + proc p5(a:string) = p4(a) + proc p6(a:string) = p5(a) + proc p7(a:string) = p6(a) + proc p8(a:string) = p7(a) + proc p9(a:string) = p8(a) + proc p10(a:string) = p9(a) + proc p11(a:string) = p10(a) + proc p12(a:string) = p11(a) + proc p13(a:string) = p12(a) + proc p14(a:string) = p13(a) + proc p15(a:string) = p14(a) + proc p16(a:string) = p15(a) + proc p17(a:string) = p16(a) + proc p18(a:string) = p17(a) + proc p19(a:string) = p18(a) + proc p20(a:string) = p19(a) let input = $raw_input - for a in input.split (): - p20 (a) - p19 (a) - - - p18 (a) - p17 (a) - p16 (a) - p15 (a) - p14 (a) - p13 (a) - p12 (a) - p11 (a) - p10 (a) - p9 (a) - p8 (a) - p7 (a) - p6 (a) - p5 (a) - p4 (a) - p3 (a) - p2 (a) - p1 (a) + for a in input.split(): + p20(a) + p19(a) + + + p18(a) + p17(a) + p16(a) + p15(a) + p14(a) + p13(a) + p12(a) + p11(a) + p10(a) + p9(a) + p8(a) + p7(a) + p6(a) + p5(a) + p4(a) + p3(a) + p2(a) + p1(a) echo output -macro macrobust (raw_input: expr) : stmt = +macro macrobust(raw_input: untyped): untyped = var output = "" - proc p1 (a:string) = - output.add (a) - - proc p2 (a:string) = p1 (a) - proc p3 (a:string) = p2 (a) - proc p4 (a:string) = p3 (a) - proc p5 (a:string) = p4 (a) - proc p6 (a:string) = p5 (a) - proc p7 (a:string) = p6 (a) - proc p8 (a:string) = p7 (a) - proc p9 (a:string) = p8 (a) - proc p10 (a:string) = p9 (a) - proc p11 (a:string) = p10 (a) - proc p12 (a:string) = p11 (a) - proc p13 (a:string) = p12 (a) - proc p14 (a:string) = p13 (a) - proc p15 (a:string) = p14 (a) - proc p16 (a:string) = p15 (a) - proc p17 (a:string) = p16 (a) - proc p18 (a:string) = p17 (a) - proc p19 (a:string) = p18 (a) - proc p20 (a:string) = p19 (a) + proc p1(a:string) = + output.add(a) + + proc p2(a:string) = p1(a) + proc p3(a:string) = p2(a) + proc p4(a:string) = p3(a) + proc p5(a:string) = p4(a) + proc p6(a:string) = p5(a) + proc p7(a:string) = p6(a) + proc p8(a:string) = p7(a) + proc p9(a:string) = p8(a) + proc p10(a:string) = p9(a) + proc p11(a:string) = p10(a) + proc p12(a:string) = p11(a) + proc p13(a:string) = p12(a) + proc p14(a:string) = p13(a) + proc p15(a:string) = p14(a) + proc p16(a:string) = p15(a) + proc p17(a:string) = p16(a) + proc p18(a:string) = p17(a) + proc p19(a:string) = p18(a) + proc p20(a:string) = p19(a) let input = $raw_input - for a in input.split (): - p20 (a) - p19 (a) - - p18 (a) - p17 (a) - p16 (a) - p15 (a) - p14 (a) - p13 (a) - p12 (a) - p11 (a) - p10 (a) - p9 (a) - p8 (a) - p7 (a) - p6 (a) - p5 (a) - p4 (a) - p3 (a) - p2 (a) + for a in input.split(): + p20(a) + p19(a) + + p18(a) + p17(a) + p16(a) + p15(a) + p14(a) + p13(a) + p12(a) + p11(a) + p10(a) + p9(a) + p8(a) + p7(a) + p6(a) + p5(a) + p4(a) + p3(a) + p2(a) echo output discard result @@ -134,4 +134,4 @@ macrobust0 """ sdafsdaffsda sdfasadfsadf fsdasdafsdfa sdfasdfafsda sdfasdafsadf sdfasdafsdaf sdfasdafsdaf -""" \ No newline at end of file +""" diff --git a/tests/collections/ttableconstr.nim b/tests/collections/ttableconstr.nim index a9262e70e..884378a76 100644 --- a/tests/collections/ttableconstr.nim +++ b/tests/collections/ttableconstr.nim @@ -1,6 +1,6 @@ # Test if the new table constructor syntax works: -template ignoreExpr(e: expr): stmt {.immediate.} = +template ignoreExpr(e) = discard # test first class '..' syntactical citizen: diff --git a/tests/compiles/tcompiles.nim b/tests/compiles/tcompiles.nim index b3d9c17ce..1a21315c8 100644 --- a/tests/compiles/tcompiles.nim +++ b/tests/compiles/tcompiles.nim @@ -1,13 +1,15 @@ # test the new 'compiles' feature: -template supports(opr, x: expr): bool {.immediate.} = +template supports(opr, x: untyped): bool = compiles(opr(x)) or compiles(opr(x, x)) -template ok(x: expr): stmt = - static: assert(x) +template ok(x) = + static: + assert(x) -template no(x: expr): stmt = - static: assert(not x) +template no(x) = + static: + assert(not x) type TObj = object diff --git a/tests/concepts/matrix.nim b/tests/concepts/matrix.nim index a30cbe4f3..47a709f80 100644 --- a/tests/concepts/matrix.nim +++ b/tests/concepts/matrix.nim @@ -9,7 +9,6 @@ proc `[]=`*(M: var Matrix; m, n: int; v: M.T) = M.data[m * M.N + n] = v # Adapt the Matrix type to the concept's requirements -template Rows*(M: type Matrix): expr = M.M -template Cols*(M: type Matrix): expr = M.N +template Rows*(M: type Matrix): untyped = M.M +template Cols*(M: type Matrix): untyped = M.N template ValueType*(M: type Matrix): typedesc = M.T - diff --git a/tests/concepts/t3330.nim b/tests/concepts/t3330.nim index 04add2b6f..fcd5054ef 100644 --- a/tests/concepts/t3330.nim +++ b/tests/concepts/t3330.nim @@ -2,16 +2,16 @@ discard """ errormsg: "type mismatch: got (Bar[system.int])" nimout: ''' t3330.nim(40, 4) Error: type mismatch: got (Bar[system.int]) -but expected one of: +but expected one of: proc test(foo: Foo[int]) t3330.nim(25, 8) Hint: Non-matching candidates for add(k, string, T) -proc add[T](x: var seq[T]; y: T) -proc add(result: var string; x: float) proc add(x: var string; y: string) -proc add(x: var string; y: cstring) proc add(x: var string; y: char) proc add(result: var string; x: int64) +proc add(x: var string; y: cstring) +proc add(result: var string; x: float) proc add[T](x: var seq[T]; y: openArray[T]) +proc add[T](x: var seq[T]; y: T) t3330.nim(25, 8) template/generic instantiation from here t3330.nim(32, 6) Foo: 'bar.value' cannot be assigned to diff --git a/tests/concepts/tmanual.nim b/tests/concepts/tmanual.nim index 43290a6ad..c917f5022 100644 --- a/tests/concepts/tmanual.nim +++ b/tests/concepts/tmanual.nim @@ -14,10 +14,10 @@ t ''' """ -template accept(e: expr) = +template accept(e) = static: assert compiles(e) -template reject(e: expr) = +template reject(e) = static: assert(not compiles(e)) type @@ -40,4 +40,3 @@ takesContainer(@[4, 5, 6]) takesContainer(@["a", "b"]) takesContainer "test" reject takesContainer(10) - diff --git a/tests/concepts/tmodifiersinplace.nim b/tests/concepts/tmodifiersinplace.nim new file mode 100644 index 000000000..db5583929 --- /dev/null +++ b/tests/concepts/tmodifiersinplace.nim @@ -0,0 +1,30 @@ +type + VarContainer[T] = concept c + put(var c, T) + + AltVarContainer[T] = concept var c + put(c, T) + + NonVarContainer[T] = concept c + put(c, T) + + GoodContainer = object + x: int + + BadContainer = object + x: int + +proc put(x: BadContainer, y: int) = discard +proc put(x: var GoodContainer, y: int) = discard + +template ok(x) = assert(x) +template no(x) = assert(not(x)) + +static: + ok GoodContainer is VarContainer[int] + ok GoodContainer is AltVarContainer[int] + no BadContainer is VarContainer[int] + no BadContainer is AltVarContainer[int] + ok GoodContainer is NonVarContainer[int] + ok BadContainer is NonVarContainer[int] + diff --git a/tests/concepts/tstackconcept.nim b/tests/concepts/tstackconcept.nim index b6ead2c2b..2238dacb6 100644 --- a/tests/concepts/tstackconcept.nim +++ b/tests/concepts/tstackconcept.nim @@ -12,7 +12,7 @@ IMPLICIT VALUE TYPE NAME INT INT import typetraits, strutils -template reject(e: expr) = +template reject(e) = static: assert(not compiles(e)) type @@ -60,4 +60,3 @@ reject s.genericAlgorithm "x" reject s.genericAlgorithm 1.0 reject "str".implicitGeneric reject implicitGeneric(10) - diff --git a/tests/constructors/t5965_1.nim b/tests/constructors/t5965_1.nim new file mode 100644 index 000000000..9f947f859 --- /dev/null +++ b/tests/constructors/t5965_1.nim @@ -0,0 +1,10 @@ +discard """ + file: "t5965_1.nim" + line: 10 + errormsg: "incorrect object construction syntax" +""" + +type Foo = object + a, b, c: int + +discard Foo(a: 1, 2) diff --git a/tests/constructors/t5965_2.nim b/tests/constructors/t5965_2.nim new file mode 100644 index 000000000..a3f7174c9 --- /dev/null +++ b/tests/constructors/t5965_2.nim @@ -0,0 +1,10 @@ +discard """ + file: "t5965_2.nim" + line: 10 + errormsg: "incorrect object construction syntax" +""" + +type Foo = object + a: int + +discard Foo(a: 1, 2) diff --git a/tests/cpp/ttypeinfo.nim b/tests/cpp/ttypeinfo.nim index 1529c86e9..282c682b2 100644 --- a/tests/cpp/ttypeinfo.nim +++ b/tests/cpp/ttypeinfo.nim @@ -1,5 +1,22 @@ discard """ + output: '''100''' cmd: "nim cpp $file" """ import typeinfo + +#bug #6016 +type + Onion {.union.} = object + field1: int + field2: uint64 + + Stroom = Onion + + PStroom = ptr Stroom + +proc pstruct(u: PStroom) = + echo u.field2 + +var x = Onion(field1: 100) +pstruct(x.addr) \ No newline at end of file diff --git a/tests/discard/tdiscardable.nim b/tests/discard/tdiscardable.nim index 99adcfd30..662d2725a 100644 --- a/tests/discard/tdiscardable.nim +++ b/tests/discard/tdiscardable.nim @@ -13,7 +13,7 @@ q[float](0.8, 0.2) # bug #942 -template maybeMod(x: Tinteger, module:Natural):expr = +template maybeMod(x: Tinteger, module:Natural): untyped = if module > 0: x mod module else: x diff --git a/tests/exception/tdefer1.nim b/tests/exception/tdefer1.nim index cb3d09b01..b84ba7681 100644 --- a/tests/exception/tdefer1.nim +++ b/tests/exception/tdefer1.nim @@ -10,7 +10,7 @@ A''' # bug #1742 -template test(): expr = +template test(): untyped = let a = 0 defer: echo "hi" a @@ -29,7 +29,7 @@ template atFuncEnd = defer: echo "B" -template testB(): expr = +template testB(): untyped = let a = 0 defer: echo "hi" # Delete this line to make it work a diff --git a/tests/exception/tfinally3.nim b/tests/exception/tfinally3.nim index 037ca9553..6098672a2 100644 --- a/tests/exception/tfinally3.nim +++ b/tests/exception/tfinally3.nim @@ -1,10 +1,7 @@ discard """ file: "tfinally3.nim" - output: '''false -Within finally->try -Traceback (most recent call last) -tfinally3.nim(24) tfinally3 -Error: unhandled exception: First [Exception]''' + outputsub: '''false +Within finally->try''' exitCode: 1 """ # Test break in try statement: diff --git a/tests/fields/tfields_in_template.nim b/tests/fields/tfields_in_template.nim index 9352a7a51..b7d5d2343 100644 --- a/tests/fields/tfields_in_template.nim +++ b/tests/fields/tfields_in_template.nim @@ -9,7 +9,7 @@ for name, value in (n: "v").fieldPairs: echo name # This doesn't compile - "expression 'name' has no type (or is ambiguous)". -template wrapper: stmt = +template wrapper: typed = for name, value in (n: "v").fieldPairs: echo name wrapper() diff --git a/tests/float/tfloat7.nim b/tests/float/tfloat7.nim index 2337d1dd4..5fd0d43d9 100644 --- a/tests/float/tfloat7.nim +++ b/tests/float/tfloat7.nim @@ -10,7 +10,7 @@ passed.''' """ import strutils -template expect_fail(x: expr) = +template expect_fail(x) = try: discard x echo("expected to fail!") diff --git a/tests/generics/tgeneric3.nim b/tests/generics/tgeneric3.nim index 0dbd5b03c..d014eb998 100644 --- a/tests/generics/tgeneric3.nim +++ b/tests/generics/tgeneric3.nim @@ -66,7 +66,7 @@ proc setItem[T,D](Akey: T, Avalue: D, ANode: PNode[T,D]): ref TItem[T,D] {.inlin proc cmp[T:int8|int16|int32|int64|int] (a,b: T): T {.inline.} = return a-b -template binSearchImpl *(docmp: expr) {.immediate.} = +template binSearchImpl *(docmp: untyped) = var bFound = false result = 0 var H = haystack.len - 1 diff --git a/tests/generics/tgenerictmpl.nim b/tests/generics/tgenerictmpl.nim index c71ce4e2e..e18f020c2 100644 --- a/tests/generics/tgenerictmpl.nim +++ b/tests/generics/tgenerictmpl.nim @@ -5,7 +5,7 @@ discard """ # bug #3498 -template defaultOf[T](t: T): expr = (var d: T; d) +template defaultOf[T](t: T): untyped = (var d: T; d) echo defaultOf(1) #<- excpected 0 diff --git a/tests/generics/tgenerictmpl2.nim b/tests/generics/tgenerictmpl2.nim index 0ecaf9ded..ac92d3281 100644 --- a/tests/generics/tgenerictmpl2.nim +++ b/tests/generics/tgenerictmpl2.nim @@ -27,5 +27,5 @@ ttmpl[int] #<- crash case #3 # but still allow normal use of [] on non-generic templates -template tarr: expr = [1, 2, 3, 4] +template tarr: untyped = [1, 2, 3, 4] echo tarr[1] diff --git a/tests/generics/tunique_type.nim b/tests/generics/tunique_type.nim index da2f9e4b2..ccb367ac8 100644 --- a/tests/generics/tunique_type.nim +++ b/tests/generics/tunique_type.nim @@ -36,7 +36,7 @@ proc derefExpr(exprRef: string): NimNode {.compileTime.} = type Mapped[Input; predicate: static[string]] = object input: Input -macro map(input, predicate: expr): expr = +macro map(input, predicate: untyped): untyped = let predicate = callsite()[2] newNimNode(nnkObjConstr).add( newNimNode(nnkBracketExpr).add( @@ -47,7 +47,7 @@ macro map(input, predicate: expr): expr = ident"input", input)) proc `[]`(m: Mapped, i: int): auto = - macro buildResult: expr = + macro buildResult: untyped = newCall( derefExpr(m.predicate), newNimNode(nnkBracketExpr).add( diff --git a/tests/gensym/tgensym.nim b/tests/gensym/tgensym.nim index 3c85b0b83..e33a2783f 100644 --- a/tests/gensym/tgensym.nim +++ b/tests/gensym/tgensym.nim @@ -2,7 +2,7 @@ discard """ output: "123100" """ -template hygienic(val: expr) = +template hygienic(val) = var x = val stdout.write x diff --git a/tests/gensym/tgensymgeneric.nim b/tests/gensym/tgensymgeneric.nim index 54390a4ef..9963ba808 100644 --- a/tests/gensym/tgensymgeneric.nim +++ b/tests/gensym/tgensymgeneric.nim @@ -1,3 +1,7 @@ +discard """ + output: '''true''' +""" + # We need to open the gensym'ed symbol again so that the instantiation # creates a fresh copy; but this is wrong the very first reason for gensym # is that scope rules cannot be used! So simply removing 'sfGenSym' does @@ -28,4 +32,23 @@ var let x = gen(a) let y = gen(b) -echo x.x, " ", y.x +doAssert x.x == 123 +doAssert y.x == "abc" + +# test symbol equality + +import macros + +static: + let sym1 = genSym() + let sym2 = genSym() + let sym3 = sym1 + let nimsym = sym1.symbol + doAssert sym1 == sym1 + doAssert sym2 != sym3 + doAssert sym2.symbol != sym3.symbol + doAssert sym3 == sym1 + doAssert sym1.symbol == sym1.symbol + doAssert nimsym == nimsym + +echo "true" diff --git a/tests/iter/tclosureiters.nim b/tests/iter/tclosureiters.nim index 0eb624a8c..37313d4d7 100644 --- a/tests/iter/tclosureiters.nim +++ b/tests/iter/tclosureiters.nim @@ -18,7 +18,8 @@ discard """ 0 0 1 -2''' +2 +70''' """ when true: @@ -71,3 +72,10 @@ for x in infinite.take(3): let inf = infinite for x in inf.take(3): echo x + +# bug #3583 +proc foo(f: (iterator(): int)) = + for i in f(): echo i + +let fIt = iterator(): int = yield 70 +foo fIt diff --git a/tests/js/tjsffi.nim b/tests/js/tjsffi.nim index e4aad4b99..325ab6366 100644 --- a/tests/js/tjsffi.nim +++ b/tests/js/tjsffi.nim @@ -309,11 +309,11 @@ block: on("click") do (e: Event): console.log e - jslib.on "reloaded" do: + jslib.on("reloaded") do: console.log jsarguments[0] # this test case is different from the above, because # `subscribe` is not overloaded in the current scope - jslib.subscribe "updates": + jslib.subscribe("updates"): console.log jsarguments[0] diff --git a/tests/js/ttryexceptnewsyntax.nim b/tests/js/ttryexceptnewsyntax.nim new file mode 100644 index 000000000..2573c3727 --- /dev/null +++ b/tests/js/ttryexceptnewsyntax.nim @@ -0,0 +1,13 @@ +discard """ + output: '''hello''' +""" + +type + MyException = ref Exception + +#bug #5986 + +try: + raise MyException(msg: "hello") +except MyException as e: + echo e.msg diff --git a/tests/lexer/tind1.nim b/tests/lexer/tind1.nim index 6a975d5be..8a2aea9b2 100644 --- a/tests/lexer/tind1.nim +++ b/tests/lexer/tind1.nim @@ -11,17 +11,15 @@ var x = if 4 != 5: else: "no" -macro mymacro(n: expr): stmt {.immediate.} = nil +macro mymacro(n): untyped {.immediate.} = + discard mymacro: echo "test" else: echo "else part" - if 4 == 3: echo "bug" else: echo "no bug" - - diff --git a/tests/macros/tbindsym.nim b/tests/macros/tbindsym.nim index e1e3b5112..6289d3eb2 100644 --- a/tests/macros/tbindsym.nim +++ b/tests/macros/tbindsym.nim @@ -11,7 +11,7 @@ type TTextKind = enum TFoo, TBar -macro test: stmt = +macro test: untyped = var x = @[TFoo, TBar] result = newStmtList() for i in x: diff --git a/tests/macros/tbugs.nim b/tests/macros/tbugs.nim index 1bfce6bc4..990edf1e2 100644 --- a/tests/macros/tbugs.nim +++ b/tests/macros/tbugs.nim @@ -26,7 +26,7 @@ iterator test2(f: string): Foo = for i in f: yield Foo(s: i) -macro test(): stmt = +macro test(): untyped = for i in test2("asdf"): echo i.s @@ -39,7 +39,7 @@ import macros type TType = tuple[s: string] -macro echotest(): stmt = +macro echotest(): untyped = var t: TType t.s = "" t.s.add("test") @@ -61,7 +61,7 @@ proc get_data(d: Td) : string {.compileTime.} = #result.add("aa") # B #result = result & "aa" # C -macro m(s:static[Td]) : stmt = +macro m(s:static[Td]) : untyped = echo get_data(s) echo get_data(s) result = newEmptyNode() @@ -77,7 +77,7 @@ proc nilcheck(): NimNode {.compileTime.} = echo(result.isNil) # true echo(repr(result)) # nil -macro testnilcheck(): stmt = +macro testnilcheck(): untyped = result = newNimNode(nnkStmtList) discard nilcheck() @@ -95,10 +95,10 @@ echo c[0] # bug #3046 -macro sampleMacroInt(i: int): stmt = +macro sampleMacroInt(i: int): untyped = echo i.intVal -macro sampleMacroBool(b: bool): stmt = +macro sampleMacroBool(b: bool): untyped = echo b.boolVal sampleMacroInt(42) diff --git a/tests/macros/tcomplexecho.nim b/tests/macros/tcomplexecho.nim index f7f933c1c..0b70a3ef7 100644 --- a/tests/macros/tcomplexecho.nim +++ b/tests/macros/tcomplexecho.nim @@ -10,7 +10,7 @@ OK import macros # Bug from the forum -macro addEcho1(s: untyped): stmt = +macro addEcho1(s: untyped): untyped = s.body.add(newCall("echo", newStrLitNode("OK"))) result = s @@ -32,7 +32,7 @@ proc foo(): seq[NimNode] {.compiletime.} = result.add test() result.add parseExpr("echo(5+56)") -macro bar(): stmt = +macro bar(): typed = result = newNimNode(nnkStmtList) let x = foo() for xx in x: diff --git a/tests/macros/tdebugstmt.nim b/tests/macros/tdebugstmt.nim index 421f8fd14..740ae7b05 100644 --- a/tests/macros/tdebugstmt.nim +++ b/tests/macros/tdebugstmt.nim @@ -6,7 +6,7 @@ x: some string''' import macros -macro debug(n: varargs[expr]): stmt = +macro debug(n: varargs[untyped]): untyped = # `n` is a Nim AST that contains the whole macro invocation # this macro returns a list of statements: result = newNimNode(nnkStmtList, n) diff --git a/tests/macros/tdumpast2.nim b/tests/macros/tdumpast2.nim index 6b694fa77..c4c591b2a 100644 --- a/tests/macros/tdumpast2.nim +++ b/tests/macros/tdumpast2.nim @@ -21,7 +21,7 @@ proc dumpit(n: NimNode): string {.compileTime.} = add(result, dumpit(n[j])) add(result, ")") -macro dumpAST(n: stmt): stmt {.immediate.} = +macro dumpAST(n): untyped = # dump AST as a side-effect and return the inner node let n = callsite() echo dumpit(n) diff --git a/tests/macros/tdumpastgen.nim b/tests/macros/tdumpastgen.nim new file mode 100644 index 000000000..faed77225 --- /dev/null +++ b/tests/macros/tdumpastgen.nim @@ -0,0 +1,25 @@ +discard """ +msg: '''nnkStmtList.newTree( + nnkVarSection.newTree( + nnkIdentDefs.newTree( + newIdentNode(!"x"), + newEmptyNode(), + nnkCall.newTree( + nnkDotExpr.newTree( + newIdentNode(!"foo"), + newIdentNode(!"create") + ), + newLit(56) + ) + ) + ) +)''' +""" + +# disabled; can't work as the output is done by the compiler + +import macros + +dumpAstGen: + var x = foo.create(56) + diff --git a/tests/macros/texprcolonexpr.nim b/tests/macros/texprcolonexpr.nim index 3b2c86b77..59c799771 100644 --- a/tests/macros/texprcolonexpr.nim +++ b/tests/macros/texprcolonexpr.nim @@ -13,7 +13,7 @@ Infix """ import macros -macro def(x: stmt): stmt {.immediate.} = +macro def(x): untyped = echo treeRepr(x) def name(a, b:cint) => nil diff --git a/tests/macros/tgenericparams.nim b/tests/macros/tgenericparams.nim new file mode 100644 index 000000000..d656f045a --- /dev/null +++ b/tests/macros/tgenericparams.nim @@ -0,0 +1,13 @@ +discard """ +output: '''proc foo[T, N: static[int]]() +proc foo[T; N: static[int]]()''' +""" +import macros + +macro test():string = + let expr0 = "proc foo[T, N: static[int]]()" + let expr1 = "proc foo[T; N: static[int]]()" + + $toStrLit(parseExpr(expr0)) & "\n" & $toStrLit(parseExpr(expr1)) + +echo test() diff --git a/tests/macros/tgensym.nim b/tests/macros/tgensym.nim index a4d1a3606..955168939 100644 --- a/tests/macros/tgensym.nim +++ b/tests/macros/tgensym.nim @@ -11,7 +11,7 @@ proc convertReturns(node, retFutureSym: NimNode): NimNode {.compileTime.} = for i in 0 .. <node.len: result[i] = convertReturns(node[i], retFutureSym) -macro async2(prc: stmt): stmt {.immediate.} = +macro async2(prc: untyped): untyped = expectKind(prc, nnkProcDef) var outerProcBody = newNimNode(nnkStmtList) diff --git a/tests/macros/tgentemplates.nim b/tests/macros/tgentemplates.nim index 764b94bc7..301d58c6a 100644 --- a/tests/macros/tgentemplates.nim +++ b/tests/macros/tgentemplates.nim @@ -20,7 +20,7 @@ proc parse_template(node: NimNode, value: string) {.compiletime.} = while index < value.len and parse_until_symbol(node, value, index): discard -macro tmpli*(body: expr): stmt = +macro tmpli*(body: untyped): typed = result = newStmtList() result.add parseExpr("result = \"\"") result.parse_template body[1].strVal diff --git a/tests/macros/tgettype.nim b/tests/macros/tgettype.nim index 0eab6c0a0..fa02bce57 100644 --- a/tests/macros/tgettype.nim +++ b/tests/macros/tgettype.nim @@ -10,11 +10,18 @@ type name : string password : string -macro testUser: expr = - return newLit(User.getType.lispRepr) +macro testUser: string = + result = newLit(User.getType.lispRepr) -macro testGeneric(T: typedesc[Model]): expr = - return newLit(T.getType.lispRepr) +macro testGeneric(T: typedesc[Model]): string= + result = newLit(T.getType.lispRepr) echo testUser echo User.testGeneric + +macro assertVoid(e: typed): untyped = + assert(getTypeInst(e).typeKind == ntyVoid) + +proc voidProc() = discard + +assertVoid voidProc() diff --git a/tests/macros/tgettypeinst.nim b/tests/macros/tgettypeinst.nim index f89aa5e0b..8e1d9bc13 100644 --- a/tests/macros/tgettypeinst.nim +++ b/tests/macros/tgettypeinst.nim @@ -22,7 +22,7 @@ proc symToIdent(x: NimNode): NimNode = result.add symToIdent(c) # check getTypeInst and getTypeImpl for given symbol x -macro testX(x,inst0: typed; recurse: static[bool]; implX: stmt): typed = +macro testX(x,inst0: typed; recurse: static[bool]; implX: typed): typed = # check that getTypeInst(x) equals inst0 let inst = x.getTypeInst let instr = inst.symToIdent.treeRepr diff --git a/tests/macros/tidgen.nim b/tests/macros/tidgen.nim index 2fe9e0f82..88322b227 100644 --- a/tests/macros/tidgen.nim +++ b/tests/macros/tidgen.nim @@ -8,7 +8,7 @@ import macros var gid {.compileTime.} = 3 -macro genId(): expr = +macro genId(): int = result = newIntLitNode(gid) inc gid diff --git a/tests/macros/tmacro1.nim b/tests/macros/tmacro1.nim index 2dd5c31df..ac6bd02a5 100644 --- a/tests/macros/tmacro1.nim +++ b/tests/macros/tmacro1.nim @@ -2,7 +2,7 @@ import macros from uri import `/` -macro test*(a: stmt): stmt {.immediate.} = +macro test*(a: untyped): untyped = var nodes: tuple[a, b: int] nodes.a = 4 nodes[1] = 45 @@ -20,4 +20,3 @@ macro test*(a: stmt): stmt {.immediate.} = test: "hi" - diff --git a/tests/macros/tmacro2.nim b/tests/macros/tmacro2.nim index 17f312925..72972c0c1 100644 --- a/tests/macros/tmacro2.nim +++ b/tests/macros/tmacro2.nim @@ -12,7 +12,7 @@ proc testBlock(): string {.compileTime.} = echo "outer block" result = "ta-da" -macro mac(n: expr): expr = +macro mac(n: typed): string = let n = callsite() expectKind(n, nnkCall) expectLen(n, 2) diff --git a/tests/macros/tmacro3.nim b/tests/macros/tmacro3.nim index d7421ff7f..a1dc4f371 100644 --- a/tests/macros/tmacro3.nim +++ b/tests/macros/tmacro3.nim @@ -8,7 +8,7 @@ type TA = tuple[a: int] PA = ref TA -macro test*(a: stmt): stmt {.immediate.} = +macro test*(a: untyped): untyped = var val: PA new(val) val.a = 4 @@ -16,7 +16,7 @@ macro test*(a: stmt): stmt {.immediate.} = test: "hi" -macro test2*(a: stmt): stmt {.immediate.} = +macro test2*(a: untyped): untyped = proc testproc(recurse: int) = echo "Thats weird" var o : NimNode = nil @@ -28,4 +28,3 @@ macro test2*(a: stmt): stmt {.immediate.} = test2: "hi" - diff --git a/tests/macros/tmacro4.nim b/tests/macros/tmacro4.nim index fb07941a9..164afaeb7 100644 --- a/tests/macros/tmacro4.nim +++ b/tests/macros/tmacro4.nim @@ -5,7 +5,7 @@ discard """ import macros, strutils -macro test_macro*(s: string, n: stmt): stmt {.immediate.} = +macro test_macro*(s: string, n: untyped): untyped = result = newNimNode(nnkStmtList) var ass : NimNode = newNimNode(nnkAsgn) add(ass, newIdentNode("str")) @@ -16,4 +16,3 @@ when isMainModule: test_macro(str): var i : integer = 123 echo str - diff --git a/tests/macros/tmacro5.nim b/tests/macros/tmacro5.nim index d7a4fe8c8..1c60e1ffd 100644 --- a/tests/macros/tmacro5.nim +++ b/tests/macros/tmacro5.nim @@ -3,7 +3,7 @@ import macros,json var decls{.compileTime.}: seq[NimNode] = @[] var impls{.compileTime.}: seq[NimNode] = @[] -macro importImpl_forward(name, returns): stmt {.immediate.} = +macro importImpl_forward(name, returns: untyped): untyped = result = newNimNode(nnkEmpty) var func_name = newNimNode(nnkAccQuoted) func_name.add newIdentNode("import") @@ -19,7 +19,7 @@ macro importImpl_forward(name, returns): stmt {.immediate.} = res[3].add returns var p1 = newNimNode(nnkIdentDefs) p1.add newIdentNode("dat") - p1.add newIdentNOde("PJsonNode") + p1.add newIdentNOde("JsonNode") p1.add newNimNode(nnkEmpty) res[3].add p1 var p2 = newNimNode(nnkIdentDefs) @@ -38,7 +38,7 @@ macro importImpl_forward(name, returns): stmt {.immediate.} = decls.add res echo(repr(res)) -macro importImpl(name, returns: expr, body: stmt): stmt {.immediate.} = +macro importImpl(name, returns, body: untyped): typed = #var res = getAST(importImpl_forward(name, returns)) discard getAST(importImpl_forward(name, returns)) var res = copyNimTree(decls[decls.high]) @@ -46,7 +46,7 @@ macro importImpl(name, returns: expr, body: stmt): stmt {.immediate.} = echo repr(res) impls.add res -macro okayy:stmt = +macro okayy: typed = result = newNimNode(nnkStmtList) for node in decls: result.add node for node in impls: result.add node diff --git a/tests/macros/tmacro_in_template.nim b/tests/macros/tmacro_in_template.nim index 8f7753cea..9668495df 100644 --- a/tests/macros/tmacro_in_template.nim +++ b/tests/macros/tmacro_in_template.nim @@ -2,8 +2,8 @@ # bug #1944 import macros -template t(e: expr): stmt = - macro m(eNode: expr): stmt = +template t(e: untyped): untyped = + macro m(eNode: untyped): untyped = echo eNode.treeRepr m e diff --git a/tests/macros/tmacroaspragma.nim b/tests/macros/tmacroaspragma.nim index 0e5c352b3..5f06f2425 100644 --- a/tests/macros/tmacroaspragma.nim +++ b/tests/macros/tmacroaspragma.nim @@ -1,8 +1,7 @@ import macros -macro foo(x: stmt): stmt = +macro foo(x: untyped): untyped = echo treerepr(callsite()) result = newNimNode(nnkStmtList) proc zoo() {.foo.} = echo "hi" - diff --git a/tests/macros/tmacros1.nim b/tests/macros/tmacros1.nim index 1a1073a44..9e3ab028b 100644 --- a/tests/macros/tmacros1.nim +++ b/tests/macros/tmacros1.nim @@ -5,7 +5,7 @@ discard """ import macros, strutils -macro outterMacro*(n: stmt): stmt {.immediate.} = +macro outterMacro*(n, blck: untyped): untyped = let n = callsite() var j : string = "hi" proc innerProc(i: int): string = @@ -27,5 +27,3 @@ var str: string outterMacro(str): "hellow" echo str - - diff --git a/tests/macros/tmacrostmt.nim b/tests/macros/tmacrostmt.nim index 23e2f358c..6f648958f 100644 --- a/tests/macros/tmacrostmt.nim +++ b/tests/macros/tmacrostmt.nim @@ -1,5 +1,5 @@ import macros -macro case_token(n: stmt): stmt {.immediate.} = +macro case_token(n: untyped): untyped {.immediate.} = # creates a lexical analyzer from regular expressions # ... (implementation is an exercise for the reader :-) nil @@ -18,7 +18,7 @@ case_token: inc i #bug #488 -macro foo: stmt = +macro foo: typed = var exp = newCall("whatwhat", newIntLitNode(1)) if compiles(getAst(exp)): return exp else: echo "Does not compute!" diff --git a/tests/macros/tmacrotypes.nim b/tests/macros/tmacrotypes.nim index 991668930..e8a68c34d 100644 --- a/tests/macros/tmacrotypes.nim +++ b/tests/macros/tmacrotypes.nim @@ -5,7 +5,7 @@ int''' import macros -macro checkType(ex: stmt; expected: expr): stmt = +macro checkType(ex: typed; expected: string): untyped = var t = ex.getType() echo t diff --git a/tests/macros/tnewlit.nim b/tests/macros/tnewlit.nim index 69245d076..3ba1e09e1 100644 --- a/tests/macros/tnewlit.nim +++ b/tests/macros/tnewlit.nim @@ -138,3 +138,12 @@ macro test_newLit_ComposedType: untyped = result = newLit(ct) doAssert test_newLit_ComposedType == ComposedType(mt: MyType(a: 123, b:"abc"), arr: [1,2,3,4], data: @[1.byte, 3, 7, 127]) + +macro test_newLit_empty_seq_string: untyped = + var strSeq = newSeq[string](0) + result = newLit(strSeq) + +block: + # x needs to be of type seq[string] + var x = test_newLit_empty_seq_string + x.add("xyz") diff --git a/tests/macros/tnodecompare.nim b/tests/macros/tnodecompare.nim index b9cf7df48..5ffb495b1 100644 --- a/tests/macros/tnodecompare.nim +++ b/tests/macros/tnodecompare.nim @@ -11,7 +11,7 @@ static: nodeB.strVal = "this is a different comment" doAssert nodeA != nodeB -macro test(a: typed, b: typed): expr = +macro test(a: typed, b: typed): untyped = newLit(a == b) doAssert test(1, 1) == true @@ -29,10 +29,10 @@ var a, b: int doAssert test(a, a) == true doAssert test(a, b) == false -macro test2: expr = +macro test2: untyped = newLit(bindSym"Obj" == bindSym"Obj") -macro test3: expr = +macro test3: untyped = newLit(bindSym"Obj" == bindSym"Other") doAssert test2() == true diff --git a/tests/macros/tquotewords.nim b/tests/macros/tquotewords.nim index 48fcafd62..fa00ba9de 100644 --- a/tests/macros/tquotewords.nim +++ b/tests/macros/tquotewords.nim @@ -6,7 +6,7 @@ discard """ import macros -macro quoteWords(n: varargs[expr]): expr {.immediate.} = +macro quoteWords(n: varargs[untyped]): untyped = let n = callsite() result = newNimNode(nnkBracket, n) for i in 1..n.len-1: @@ -21,6 +21,3 @@ for w in items(myWordList): s.add(w) echo s #OUT thisanexample - - - diff --git a/tests/macros/trecmacro.nim b/tests/macros/trecmacro.nim index 28b6db530..69ebe3da3 100644 --- a/tests/macros/trecmacro.nim +++ b/tests/macros/trecmacro.nim @@ -4,7 +4,7 @@ discard """ errormsg: "recursive dependency: 'dump'" """ -macro dump(n: stmt): stmt = +macro dump(n: untyped): untyped = dump(n) if kind(n) == nnkNone: nil diff --git a/tests/macros/treturnsempty.nim b/tests/macros/treturnsempty.nim index 7af26a747..a5a678af3 100644 --- a/tests/macros/treturnsempty.nim +++ b/tests/macros/treturnsempty.nim @@ -3,10 +3,9 @@ discard """ line: 11 """ # bug #2372 -macro foo(dummy: int): stmt = +macro foo(dummy: int): untyped = discard proc takeStr(s: string) = echo s takeStr foo(12) - diff --git a/tests/macros/tsametype.nim b/tests/macros/tsametype.nim index 34296015f..865fb4cb8 100644 --- a/tests/macros/tsametype.nim +++ b/tests/macros/tsametype.nim @@ -13,7 +13,7 @@ false''' import macros -macro same(a: typedesc, b: typedesc): expr = +macro same(a: typedesc, b: typedesc): untyped = newLit(a.getType[1].sameType b.getType[1]) echo same(int, int) diff --git a/tests/macros/tstaticparamsmacro.nim b/tests/macros/tstaticparamsmacro.nim index f6ecb3a9f..ea59936e0 100644 --- a/tests/macros/tstaticparamsmacro.nim +++ b/tests/macros/tstaticparamsmacro.nim @@ -25,7 +25,7 @@ type const data: Tconfig = (@["aa", "bb"], @[11, 22]) -macro mymacro(data: static[TConfig]): stmt = +macro mymacro(data: static[TConfig]): untyped = echo "letters" for s in items(data.letters): echo s @@ -43,10 +43,10 @@ const a : Ta = @[(11, 22), (33, 44)] b : Tb = (@[55,66], @[77, 88]) -macro mA(data: static[Ta]): stmt = +macro mA(data: static[Ta]): untyped = echo "AST a \n", repr(data) -macro mB(data: static[Tb]): stmt = +macro mB(data: static[Tb]): untyped = echo "AST b \n", repr(data) echo data.e[0] @@ -56,13 +56,13 @@ mB(b) type Foo[N: static[int], Z: static[string]] = object -macro staticIntMacro(f: static[int]): stmt = echo f +macro staticIntMacro(f: static[int]): untyped = echo f staticIntMacro 10 var x: Foo[20, "Test"] -macro genericMacro[N; Z: static[string]](f: Foo[N, Z], ll = 3, zz = 12): stmt = +macro genericMacro[N; Z: static[string]](f: Foo[N, Z], ll = 3, zz = 12): untyped = echo N, Z genericMacro x diff --git a/tests/macros/tstringinterp.nim b/tests/macros/tstringinterp.nim index bc79cdaba..305f40ac5 100644 --- a/tests/macros/tstringinterp.nim +++ b/tests/macros/tstringinterp.nim @@ -9,7 +9,7 @@ proc concat(strings: varargs[string]): string = result = newString(0) for s in items(strings): result.add(s) -template processInterpolations(e: expr) = +template processInterpolations(e) = var s = e[1].strVal for f in interpolatedFragments(s): case f.kind @@ -17,7 +17,7 @@ template processInterpolations(e: expr) = of ikDollar: addDollar() of ikVar, ikExpr: addExpr(newCall("$", parseExpr(f.value))) -macro formatStyleInterpolation(e: expr): expr = +macro formatStyleInterpolation(e: untyped): untyped = let e = callsite() var formatString = "" @@ -41,7 +41,7 @@ macro formatStyleInterpolation(e: expr): expr = result[1].strVal = formatString result[2] = arrayNode -macro concatStyleInterpolation(e: expr): expr = +macro concatStyleInterpolation(e: untyped): untyped = let e = callsite() var args: seq[NimNode] newSeq(args, 0) @@ -71,4 +71,3 @@ var s2 = formatStyleInterpolation"Hello ${bob}, ${sum(alice.len, bob.len, 2)}$$" write(stdout, s1 & " | " & s2) - diff --git a/tests/macros/ttryparseexpr.nim b/tests/macros/ttryparseexpr.nim index c7bbc8e5b..54442b662 100644 --- a/tests/macros/ttryparseexpr.nim +++ b/tests/macros/ttryparseexpr.nim @@ -5,7 +5,7 @@ discard """ # feature request #1473 import macros -macro test(text: string): expr = +macro test(text: string): untyped = try: result = parseExpr(text.strVal) except ValueError: diff --git a/tests/macros/tvarnimnode.nim b/tests/macros/tvarnimnode.nim index ab0f66caa..26c9b1f1a 100644 --- a/tests/macros/tvarnimnode.nim +++ b/tests/macros/tvarnimnode.nim @@ -10,7 +10,7 @@ proc test(f: var NimNode) {.compileTime.} = f = newNimNode(nnkStmtList) f.add newCall(newIdentNode("echo"), newLit(10)) -macro blah(prc: stmt): stmt = +macro blah(prc: untyped): untyped = result = prc test(result) diff --git a/tests/macros/typesapi.nim b/tests/macros/typesapi.nim index 670b39c9e..cdbfc0ad2 100644 --- a/tests/macros/typesapi.nim +++ b/tests/macros/typesapi.nim @@ -8,7 +8,7 @@ proc (x: int) => typeDesc[proc[void, int]]''' import macros -macro showType(t:stmt): stmt = +macro showType(t:typed): untyped = let ty = t.getType echo t.repr, " => ", ty.repr diff --git a/tests/macros/typesapi2.nim b/tests/macros/typesapi2.nim index 2e59d2154..0130049c0 100644 --- a/tests/macros/typesapi2.nim +++ b/tests/macros/typesapi2.nim @@ -2,7 +2,7 @@ # be used as a type import macros -macro testTypesym (t:stmt): expr = +macro testTypesym (t:typed): untyped = var ty = t.getType if ty.typekind == ntyTypedesc: # skip typedesc get to the real type @@ -31,7 +31,7 @@ static: assert(ref int is testTypesym(ref int)) static: assert(void is testTypesym(void)) -macro tts2 (t:stmt, idx:int): expr = +macro tts2 (t:typed, idx:int): untyped = var ty = t.getType if ty.typekind == ntyTypedesc: # skip typedesc get to the real type @@ -46,4 +46,3 @@ static: assert(tts2(TestFN2, 1) is string) assert(tts2(TestFN2, 2) is int) assert(tts2(TestFN2, 3) is float) - diff --git a/tests/manyloc/keineschweine/keineschweine.nim b/tests/manyloc/keineschweine/keineschweine.nim index 804a22852..c0f1bc031 100644 --- a/tests/manyloc/keineschweine/keineschweine.nim +++ b/tests/manyloc/keineschweine/keineschweine.nim @@ -212,9 +212,10 @@ proc free(obj: PLiveBullet) = obj.record = nil -template newExplosion(obj, animation): stmt = +template newExplosion(obj, animation) = explosions.add(newAnimation(animation, AnimOnce, obj.body.getPos.cp2sfml, obj.body.getAngle)) -template newExplosion(obj, animation, angle): stmt = + +template newExplosion(obj, animation, angle) = explosions.add(newAnimation(animation, AnimOnce, obj.body.getPos.cp2sfml, angle)) proc explode*(b: PLiveBullet) = diff --git a/tests/metatype/tbindtypedesc.nim b/tests/metatype/tbindtypedesc.nim index 4f027407b..b287aad01 100644 --- a/tests/metatype/tbindtypedesc.nim +++ b/tests/metatype/tbindtypedesc.nim @@ -16,10 +16,10 @@ type TBar = tuple x, y: int -template accept(e: expr) = +template accept(e) = static: assert(compiles(e)) -template reject(e: expr) = +template reject(e) = static: assert(not compiles(e)) proc genericParamRepeated[T: typedesc](a: T, b: T) = diff --git a/tests/metatype/tmatrix.nim b/tests/metatype/tmatrix.nim index 5acd4389e..4fd32a932 100644 --- a/tests/metatype/tmatrix.nim +++ b/tests/metatype/tmatrix.nim @@ -9,7 +9,7 @@ type data: seq[float] fWidth, fHeight: int -template `|`(x, y: int): expr = y * m.fWidth + x +template `|`(x, y: int): untyped = y * m.fWidth + x proc createMatrix*(width, height: int): TMatrix = result.fWidth = width diff --git a/tests/metatype/tsemistatic.nim b/tests/metatype/tsemistatic.nim index a13175ba8..3f36abde9 100644 --- a/tests/metatype/tsemistatic.nim +++ b/tests/metatype/tsemistatic.nim @@ -7,7 +7,7 @@ type semistatic[T] = static[T] or T -template isStatic*(x): expr = +template isStatic*(x): bool = compiles(static(x)) proc foo(x: semistatic[int]) = diff --git a/tests/metatype/tstatic_overloading.nim b/tests/metatype/tstatic_overloading.nim index ce51052b7..9c065e48d 100644 --- a/tests/metatype/tstatic_overloading.nim +++ b/tests/metatype/tstatic_overloading.nim @@ -5,6 +5,6 @@ import macros proc impl(op: static[int]) = echo "impl 1 called" proc impl(op: static[int], init: int) = echo "impl 2 called" -macro wrapper2: stmt = newCall(bindSym"impl", newLit(0), newLit(0)) +macro wrapper2: untyped = newCall(bindSym"impl", newLit(0), newLit(0)) wrapper2() # Code generation for this fails. diff --git a/tests/metatype/tstaticparammacro.nim b/tests/metatype/tstaticparammacro.nim index 5c7c5e6af..bd3295874 100644 --- a/tests/metatype/tstaticparammacro.nim +++ b/tests/metatype/tstaticparammacro.nim @@ -26,7 +26,7 @@ type const data: Tconfig = (@["aa", "bb"], @[11, 22]) -macro mymacro(data: static[TConfig]): stmt = +macro mymacro(data: static[TConfig]) = echo "letters" for s in items(data.letters): echo s @@ -44,10 +44,10 @@ const a : Ta = @[(11, 22), (33, 44)] b : Tb = (@[55,66], @[77, 88]) -macro mA(data: static[Ta]): stmt = +macro mA(data: static[Ta]) = echo "AST a \n", repr(data) -macro mB(data: static[Tb]): stmt = +macro mB(data: static[Tb]) = echo "AST b \n", repr(data) echo data.e[0] @@ -57,13 +57,13 @@ mB(b) type Foo[N: static[int], Z: static[string]] = object -macro staticIntMacro(f: static[int]): stmt = echo f +macro staticIntMacro(f: static[int]) = echo f staticIntMacro 10 var x: Foo[20, "Test"] -macro genericMacro[N; Z: static[string]](f: Foo[N, Z], ll = 3, zz = 12): stmt = +macro genericMacro[N; Z: static[string]](f: Foo[N, Z], ll = 3, zz = 12) = echo N, Z genericMacro x diff --git a/tests/metatype/tstaticparams.nim b/tests/metatype/tstaticparams.nim index 69b62e4a6..ad6aa6589 100644 --- a/tests/metatype/tstaticparams.nim +++ b/tests/metatype/tstaticparams.nim @@ -74,7 +74,7 @@ matrix_2(tmat, ar2) matrix_3(tmat, ar1) matrix_4(tmat, ar2) -template reject(x): stmt = +template reject(x): untyped = static: assert(not compiles(x)) # test with arrays of wrong size diff --git a/tests/metatype/ttypedesc1.nim b/tests/metatype/ttypedesc1.nim index d78c62a94..e9eee581f 100644 --- a/tests/metatype/ttypedesc1.nim +++ b/tests/metatype/ttypedesc1.nim @@ -18,7 +18,7 @@ proc foo(T: typedesc[int or bool]): string = a = 10 result = "int or bool " & ($a) -template foo(T: typedesc[seq]): expr = "seq" +template foo(T: typedesc[seq]): string = "seq" test "types can be used as proc params": # XXX: `check` needs to know that TFoo[int, float] is a type and diff --git a/tests/metatype/ttypedesc2.nim b/tests/metatype/ttypedesc2.nim index e576ec91a..7650a6f6b 100644 --- a/tests/metatype/ttypedesc2.nim +++ b/tests/metatype/ttypedesc2.nim @@ -18,11 +18,10 @@ when true: uoffset_t* = uint32 FlatBufferBuilder* = object - uarray* {.unchecked.} [T] = array [0..0, T] Array* [T] = object o*: uoffset_t len*: int - data*: ptr uarray[T] + data*: ptr UncheckedArray[T] proc ca* (fbb: ptr FlatBufferBuilder, T: typedesc, len: int): Array[T] {.noinit.} = result.len = len diff --git a/tests/metatype/tymatrix.nim b/tests/metatype/tymatrix.nim index 7d3d52f85..14c4d8c88 100644 --- a/tests/metatype/tymatrix.nim +++ b/tests/metatype/tymatrix.nim @@ -1,6 +1,6 @@ import typetraits -template reject(e: expr) = +template reject(e) = static: assert(not compiles(e)) type diff --git a/tests/method/tmapper.nim b/tests/method/tmapper.nim index 0008d9033..a5d03f700 100644 --- a/tests/method/tmapper.nim +++ b/tests/method/tmapper.nim @@ -1,5 +1,5 @@ discard """ - errormsg: "invalid declaration order; cannot attach 'step' to method defined here: tmapper.nim(22,7)" + errormsg: "invalid declaration order; cannot attach 'step' to method defined here: tmapper.nim(22, 7)" line: 25 """ diff --git a/tests/misc/thallo.nim b/tests/misc/thallo.nim index 17e955f03..17e6089ed 100644 --- a/tests/misc/thallo.nim +++ b/tests/misc/thallo.nim @@ -15,7 +15,7 @@ proc fac[T](x: T): T = if x <= 1: return 1 else: return x.`*`(fac(x-1)) -macro macrotest(n: expr): stmt {.immediate.} = +macro macrotest(n: varargs[untyped]): untyped = let n = callsite() expectKind(n, nnkCall) expectMinLen(n, 2) @@ -24,7 +24,7 @@ macro macrotest(n: expr): stmt {.immediate.} = result.add(newCall("write", n[1], n[i])) result.add(newCall("writeLine", n[1], newStrLitNode(""))) -macro debug(n: expr): stmt {.immediate.} = +macro debug(n: untyped): untyped {.immediate.} = let n = callsite() result = newNimNode(nnkStmtList, n) for i in 1..n.len-1: @@ -82,4 +82,3 @@ for i in 2..6: when isMainModule: {.hint: "this is the main file".} - diff --git a/tests/misc/tints.nim b/tests/misc/tints.nim index 5bfb8a17c..d5374a543 100644 --- a/tests/misc/tints.nim +++ b/tests/misc/tints.nim @@ -8,7 +8,7 @@ Success''' var testNumber = 0 -template test(opr, a, b, c: expr): stmt {.immediate.} = +template test(opr, a, b, c: untyped): untyped = # test the expression at compile and runtime block: const constExpr = opr(a, b) diff --git a/tests/misc/tsimplesort.nim b/tests/misc/tsimplesort.nim index 9c6ad1207..3115863d5 100644 --- a/tests/misc/tsimplesort.nim +++ b/tests/misc/tsimplesort.nim @@ -118,7 +118,7 @@ proc toTable*[A, B](pairs: openarray[tuple[key: A, result = initTable[A, B](nextPowerOfTwo(pairs.len+10)) for key, val in items(pairs): result[key] = val -template dollarImpl(): stmt = +template dollarImpl(): typed = if t.len == 0: result = "{:}" else: @@ -305,5 +305,3 @@ proc countTableTest1 = countTableTest1() echo true - - diff --git a/tests/modules/tmismatchedvisibility.nim b/tests/modules/tmismatchedvisibility.nim index 91b639a27..2e8636d1e 100644 --- a/tests/modules/tmismatchedvisibility.nim +++ b/tests/modules/tmismatchedvisibility.nim @@ -1,6 +1,6 @@ discard """ line: 8 - errormsg: "public implementation 'tmismatchedvisibility.foo(a: int)[declared in tmismatchedvisibility.nim(6,5)]' has non-public forward declaration in " + errormsg: "public implementation 'tmismatchedvisibility.foo(a: int)[declared in tmismatchedvisibility.nim(6, 5)]' has non-public forward declaration in " """ proc foo(a: int): int diff --git a/tests/modules/treorder.nim b/tests/modules/treorder.nim new file mode 100644 index 000000000..25280c429 --- /dev/null +++ b/tests/modules/treorder.nim @@ -0,0 +1,46 @@ +discard """ + cmd: "nim -d:testdef $target $file" + output: '''works 34 +34 +defined +3''' +""" + +{.reorder: on.} +{.experimental.} + +{.push callconv: stdcall.} +proc bar(x: T) + +proc foo() = + bar(34) + whendep() + +proc foo(dummy: int) = echo dummy + +proc bar(x: T) = + echo "works ", x + foo(x) + +foo() + +type + T = int + +when defined(testdef): + proc whendep() = echo "defined" +else: + proc whendep() = echo "undefined" + +when not declared(goo): + proc goo(my, omy) = echo my + +when not declared(goo): + proc goo(my, omy) = echo omy + +using + my, omy: int + +goo(3, 4) + +{.pop.} diff --git a/tests/newconfig/tfoo.nims b/tests/newconfig/tfoo.nims index 8a709914f..8d0ed2c42 100644 --- a/tests/newconfig/tfoo.nims +++ b/tests/newconfig/tfoo.nims @@ -22,3 +22,5 @@ task default, "default target": --define: definedefine setCommand "c" +# bug #6327 +discard existsEnv("dummy") diff --git a/tests/nimble/nimbleDir/linkedPkgs/pkgA-0.1.0/pkgA.nimble-link b/tests/nimble/nimbleDir/linkedPkgs/pkgA-0.1.0/pkgA.nimble-link new file mode 100644 index 000000000..8dc825fc9 --- /dev/null +++ b/tests/nimble/nimbleDir/linkedPkgs/pkgA-0.1.0/pkgA.nimble-link @@ -0,0 +1,2 @@ +../../simplePkgs/pkgA-0.1.0/pkgA.nimble +../../simplePkgs/pkgA-0.1.0/ \ No newline at end of file diff --git a/tests/nimble/nimbleDir/linkedPkgs/pkgB-#head/pkgB.nimble-link b/tests/nimble/nimbleDir/linkedPkgs/pkgB-#head/pkgB.nimble-link new file mode 100644 index 000000000..a57a3cb66 --- /dev/null +++ b/tests/nimble/nimbleDir/linkedPkgs/pkgB-#head/pkgB.nimble-link @@ -0,0 +1,2 @@ +../../simplePkgs/pkgB-#head/pkgB.nimble +../../simplePkgs/pkgB-#head/ \ No newline at end of file diff --git a/tests/nimble/nimbleDir/linkedPkgs/pkgB-0.1.0/pkgB.nimble-link b/tests/nimble/nimbleDir/linkedPkgs/pkgB-0.1.0/pkgB.nimble-link new file mode 100644 index 000000000..9643c0fa0 --- /dev/null +++ b/tests/nimble/nimbleDir/linkedPkgs/pkgB-0.1.0/pkgB.nimble-link @@ -0,0 +1,2 @@ +../../simplePkgs/pkgB-0.1.0/pkgB.nimble +../../simplePkgs/pkgB-0.1.0/ \ No newline at end of file diff --git a/tests/nimble/nimbleDir/simplePkgs/pkgA-0.1.0/pkgA.nimble b/tests/nimble/nimbleDir/simplePkgs/pkgA-0.1.0/pkgA.nimble new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/tests/nimble/nimbleDir/simplePkgs/pkgA-0.1.0/pkgA.nimble diff --git a/tests/nimble/nimbleDir/simplePkgs/pkgA-0.1.0/pkgA/module.nim b/tests/nimble/nimbleDir/simplePkgs/pkgA-0.1.0/pkgA/module.nim new file mode 100644 index 000000000..6cb3b77d9 --- /dev/null +++ b/tests/nimble/nimbleDir/simplePkgs/pkgA-0.1.0/pkgA/module.nim @@ -0,0 +1 @@ +proc pkgATest*(): int = 1 \ No newline at end of file diff --git a/tests/nimble/nimbleDir/simplePkgs/pkgB-#head/pkgB.nimble b/tests/nimble/nimbleDir/simplePkgs/pkgB-#head/pkgB.nimble new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/tests/nimble/nimbleDir/simplePkgs/pkgB-#head/pkgB.nimble diff --git a/tests/nimble/nimbleDir/simplePkgs/pkgB-#head/pkgB/module.nim b/tests/nimble/nimbleDir/simplePkgs/pkgB-#head/pkgB/module.nim new file mode 100644 index 000000000..03d2298a2 --- /dev/null +++ b/tests/nimble/nimbleDir/simplePkgs/pkgB-#head/pkgB/module.nim @@ -0,0 +1 @@ +proc pkgBTest*(): int64 = 0xDEADBEEF \ No newline at end of file diff --git a/tests/nimble/nimbleDir/simplePkgs/pkgB-0.1.0/pkgB.nimble b/tests/nimble/nimbleDir/simplePkgs/pkgB-0.1.0/pkgB.nimble new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/tests/nimble/nimbleDir/simplePkgs/pkgB-0.1.0/pkgB.nimble diff --git a/tests/nimble/nimbleDir/simplePkgs/pkgB-0.1.0/pkgB/module.nim b/tests/nimble/nimbleDir/simplePkgs/pkgB-0.1.0/pkgB/module.nim new file mode 100644 index 000000000..56ff64197 --- /dev/null +++ b/tests/nimble/nimbleDir/simplePkgs/pkgB-0.1.0/pkgB/module.nim @@ -0,0 +1 @@ +proc pkgBTest*(): int64 = 0 \ No newline at end of file diff --git a/tests/nimble/nimbleDir/simplePkgs/pkgC-#aa11/pkgC.nimble b/tests/nimble/nimbleDir/simplePkgs/pkgC-#aa11/pkgC.nimble new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/tests/nimble/nimbleDir/simplePkgs/pkgC-#aa11/pkgC.nimble diff --git a/tests/nimble/nimbleDir/simplePkgs/pkgC-#aa11/pkgC/module.nim b/tests/nimble/nimbleDir/simplePkgs/pkgC-#aa11/pkgC/module.nim new file mode 100644 index 000000000..24a67bbe2 --- /dev/null +++ b/tests/nimble/nimbleDir/simplePkgs/pkgC-#aa11/pkgC/module.nim @@ -0,0 +1 @@ +proc pkgCTest*(): int64 = 1 \ No newline at end of file diff --git a/tests/nimble/nimbleDir/simplePkgs/pkgC-#head/pkgC.nimble b/tests/nimble/nimbleDir/simplePkgs/pkgC-#head/pkgC.nimble new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/tests/nimble/nimbleDir/simplePkgs/pkgC-#head/pkgC.nimble diff --git a/tests/nimble/nimbleDir/simplePkgs/pkgC-#head/pkgC/module.nim b/tests/nimble/nimbleDir/simplePkgs/pkgC-#head/pkgC/module.nim new file mode 100644 index 000000000..2f243ad82 --- /dev/null +++ b/tests/nimble/nimbleDir/simplePkgs/pkgC-#head/pkgC/module.nim @@ -0,0 +1 @@ +proc pkgCTest*(): int64 = 0xDEADBEEF \ No newline at end of file diff --git a/tests/nimble/readme.md b/tests/nimble/readme.md new file mode 100644 index 000000000..10fd21a83 --- /dev/null +++ b/tests/nimble/readme.md @@ -0,0 +1,2 @@ +This directory contains tests for the --nimblePath feature. + diff --git a/tests/nimble/tnimblepath.nim b/tests/nimble/tnimblepath.nim new file mode 100644 index 000000000..6ba1be1d1 --- /dev/null +++ b/tests/nimble/tnimblepath.nim @@ -0,0 +1,11 @@ +discard """ + action: run + cmd: "nim $target --nimblePath:$fileDir/nimbleDir/simplePkgs $options $file" +""" +import pkgA/module as A +import pkgB/module as B +import pkgC/module as C + +doAssert pkgATest() == 1, "Simple pkgA-0.1.0 wasn't added to path correctly." +doAssert pkgBTest() == 0xDEADBEEF, "pkgB-#head wasn't picked over pkgB-0.1.0" +doAssert pkgCTest() == 0xDEADBEEF, "pkgC-#head wasn't picked over pkgC-#aa11" \ No newline at end of file diff --git a/tests/nimble/tnimblepathlink.nim b/tests/nimble/tnimblepathlink.nim new file mode 100644 index 000000000..5b2c7cb5b --- /dev/null +++ b/tests/nimble/tnimblepathlink.nim @@ -0,0 +1,9 @@ +discard """ + action: run + cmd: "nim $target --nimblePath:$fileDir/nimbleDir/linkedPkgs $options $file" +""" +import pkgA/module as A +import pkgB/module as B + +doAssert pkgATest() == 1, "Simple linked pkgA-0.1.0 wasn't added to path correctly." +doAssert pkgBTest() == 0xDEADBEEF, "linked pkgB-#head wasn't picked over pkgB-0.1.0" \ No newline at end of file diff --git a/tests/objects/tobjconstr.nim b/tests/objects/tobjconstr.nim index 226fe98f7..12478f621 100644 --- a/tests/objects/tobjconstr.nim +++ b/tests/objects/tobjconstr.nim @@ -8,7 +8,14 @@ discard """ (k: kindA, a: (x: abc, z: [1, 7, 3]), method: ()) (k: kindA, a: (x: abc, z: [1, 8, 3]), method: ()) (k: kindA, a: (x: abc, z: [1, 9, 3]), method: ()) -(k: kindA, a: (x: abc, z: [1, 10, 3]), method: ())''' +(k: kindA, a: (x: abc, z: [1, 10, 3]), method: ()) +(x: 123) +(x: 123) +(z: 89, y: 0, x: 128) +(y: 678, x: 123) +(y: 678, x: 123) +(y: 0, x: 123) +(y: 678, x: 123)''' """ type @@ -39,3 +46,32 @@ proc main() = main() +# bug #6294 +type + A = object of RootObj + x*: int + B = object of A + y*: int + BS = object of B + C = object of BS + z*: int +# inherited fields are ignored, so no fields are set +when true: + var + o: A + o = B(x: 123) + echo o + o = B(y: 678, x: 123) + echo o + +# inherited fields are ignored +echo C(x: 128, z: 89) # (y: 0, x: 0) +echo B(y: 678, x: 123) # (y: 678, x: 0) +echo B(x: 123, y: 678) # (y: 678, x: 0) + +when true: + # correct, both with `var` and `let`; + var b=B(x: 123) + echo b # (y: 0, x: 123) + b=B(y: 678, x: 123) + echo b # (y: 678, x: 123) diff --git a/tests/objects/tobject3.nim b/tests/objects/tobject3.nim index 15dd8ea24..a24a48c8b 100644 --- a/tests/objects/tobject3.nim +++ b/tests/objects/tobject3.nim @@ -1,3 +1,13 @@ +discard """ + output: '''TBar2 +TFoo +16 +12 +16 +12''' +""" + +## XXX this output needs to be adapated for VCC which produces different results. # It turned out that it's hard to generate correct for these two test cases at # the same time. @@ -57,3 +67,44 @@ var aa = makeWindow() thisCausesError(dd, aa) +# bug #4763 +type + testObject_1 = object + size: int32 + value: int64 + + testObject_2 {.packed.} = object + size: int32 + value: int64 + + testObject_3[T] = object + size: int32 + value: T + + testObject_4 {.packed.} [T] = object + size: int32 + value: T + +echo sizeof(testObject_1) +echo sizeof(testObject_2) +echo sizeof(testObject_3[int64]) +echo sizeof(testObject_4[int64]) + +# bug #5892 +type + Foo6 = distinct array[4, float32] + AnotherFoo = distinct array[4, float32] + + AbstractAnimationSampler* = ref object of RootObj + + AnimationSampler*[T] = ref object of AbstractAnimationSampler + sampleImpl: proc(s: AnimationSampler[T], p: float): T + + ArrayAnimationSampler*[T] = ref object of AnimationSampler[T] + +proc newArrayAnimationSampler*[T](): ArrayAnimationSampler[T] = + result.new() + result.sampleImpl = nil + +discard newArrayAnimationSampler[Foo6]() +discard newArrayAnimationSampler[AnotherFoo]() diff --git a/tests/objects/toop1.nim b/tests/objects/toop1.nim index 4727d146d..f4880e3c6 100644 --- a/tests/objects/toop1.nim +++ b/tests/objects/toop1.nim @@ -35,7 +35,7 @@ proc init(my: var TRectangle) = my.height = 10 my.draw = cast[proc (my: var TFigure) {.nimcall.}](drawRectangle) -macro `!` (n: expr): stmt {.immediate.} = +macro `!` (n: untyped): typed {.immediate.}= let n = callsite() result = newNimNode(nnkCall, n) var dot = newNimNode(nnkDotExpr, n) diff --git a/tests/osproc/tworkingdir.nim b/tests/osproc/tworkingdir.nim index 84ba3375c..eeed9240d 100644 --- a/tests/osproc/tworkingdir.nim +++ b/tests/osproc/tworkingdir.nim @@ -9,7 +9,11 @@ when defined(windows): discard else: let dir1 = getCurrentDir() - var process = startProcess("/usr/bin/env", "/usr/bin", ["true"]) + var process: Process + when defined(android): + process = startProcess("/system/bin/env", "/system/bin", ["true"]) + else: + process = startProcess("/usr/bin/env", "/usr/bin", ["true"]) let dir2 = getCurrentDir() discard process.waitForExit() process.close() diff --git a/tests/overload/tparams_after_varargs.nim b/tests/overload/tparams_after_varargs.nim index a93e280b9..ad8f19ad3 100644 --- a/tests/overload/tparams_after_varargs.nim +++ b/tests/overload/tparams_after_varargs.nim @@ -1,7 +1,8 @@ discard """ output: '''a 1 b 2 x @[3, 4, 5] y 6 z 7 yay -12''' +12 +''' """ proc test(a, b: int, x: varargs[int]; y, z: int) = @@ -9,9 +10,10 @@ proc test(a, b: int, x: varargs[int]; y, z: int) = test 1, 2, 3, 4, 5, 6, 7 -template takesBlock(a, b: int, x: varargs[expr]; blck: stmt) = +# XXX maybe this should also work with ``varargs[untyped]`` +template takesBlockA(a, b: untyped; x: varargs[typed]; blck: untyped): untyped = blck echo a, b -takesBlock 1, 2, "some", 0.90, "random stuff": +takesBlockA 1, 2, "some", 0.90, "random stuff": echo "yay" diff --git a/tests/overload/tspec.nim b/tests/overload/tspec.nim index f2002a390..a84bac244 100644 --- a/tests/overload/tspec.nim +++ b/tests/overload/tspec.nim @@ -71,7 +71,7 @@ var ri: ref int gen(ri) # "ref T" -template rem(x: expr) = discard +template rem(x) = discard #proc rem[T](x: T) = discard rem unresolvedExpression(undeclaredIdentifier) diff --git a/tests/overload/tstmtoverload.nim b/tests/overload/tstmtoverload.nim index 75584bcab..7c0194e60 100644 --- a/tests/overload/tstmtoverload.nim +++ b/tests/overload/tstmtoverload.nim @@ -2,17 +2,17 @@ # bug #2481 import math -template test(loopCount: int, extraI: int, testBody: stmt): stmt = +template test(loopCount: int, extraI: int, testBody: untyped): typed = block: for i in 0..loopCount-1: testBody echo "done extraI=", extraI -template test(loopCount: int, extraF: float, testBody: stmt): stmt = +template test(loopCount: int, extraF: float, testBody: untyped): typed = block: test(loopCount, round(extraF).int, testBody) -template test(loopCount: int, testBody: stmt): stmt = +template test(loopCount: int, testBody: untyped): typed = block: test(loopCount, 0, testBody) echo "done extraI passed 0" diff --git a/tests/parallel/tblocking_channel.nim b/tests/parallel/tblocking_channel.nim new file mode 100644 index 000000000..8b8b49454 --- /dev/null +++ b/tests/parallel/tblocking_channel.nim @@ -0,0 +1,37 @@ +discard """ +output: "" +""" +import threadpool, os + +var chan: Channel[int] + +chan.open(2) +chan.send(1) +chan.send(2) +doAssert(not chan.trySend(3)) # At this point chan is at max capacity + +proc receiver() = + doAssert(chan.recv() == 1) + doAssert(chan.recv() == 2) + doAssert(chan.recv() == 3) + doAssert(chan.recv() == 4) + doAssert(chan.recv() == 5) + +var msgSent = false + +proc emitter() = + chan.send(3) + msgSent = true + +spawn emitter() +# At this point emitter should be stuck in `send` +sleep(100) # Sleep a bit to ensure that it is still stuck +doAssert(not msgSent) + +spawn receiver() +sleep(100) # Sleep a bit to let receicer consume the messages +doAssert(msgSent) # Sender should be unblocked + +doAssert(chan.trySend(4)) +chan.send(5) +sync() diff --git a/tests/parallel/tconvexhull.nim b/tests/parallel/tconvexhull.nim index dffe5339b..c4990bf2d 100644 --- a/tests/parallel/tconvexhull.nim +++ b/tests/parallel/tconvexhull.nim @@ -20,10 +20,10 @@ proc cmpPoint(a, b: Point): int = if result == 0: result = cmp(a.y, b.y) -template cross[T](o, a, b: T): expr = +template cross[T](o, a, b: T): untyped = (a.x - o.x) * (b.y - o.y) - (a.y - o.y) * (b.x - o.x) -template pro(): expr = +template pro(): untyped = while lr1 > 0 and cross(result[lr1 - 1], result[lr1], p[i]) <= 0: discard result.pop lr1 -= 1 diff --git a/tests/parallel/tguard1.nim b/tests/parallel/tguard1.nim index 3e0c131c5..c7972d225 100644 --- a/tests/parallel/tguard1.nim +++ b/tests/parallel/tguard1.nim @@ -24,7 +24,7 @@ var c.i = 89 -template atomicRead(L, x): expr = +template atomicRead(L, x): untyped = {.locks: [L].}: x diff --git a/tests/parallel/tguard2.nim b/tests/parallel/tguard2.nim index b69ea3371..661893bb5 100644 --- a/tests/parallel/tguard2.nim +++ b/tests/parallel/tguard2.nim @@ -13,7 +13,7 @@ var c.i = 89 -template atomicRead(L, x): expr = +template atomicRead(L, x): untyped = {.locks: [L].}: x diff --git a/tests/parallel/tptr_to_ref.nim b/tests/parallel/tptr_to_ref.nim index 229c247ce..fee210dcd 100644 --- a/tests/parallel/tptr_to_ref.nim +++ b/tests/parallel/tptr_to_ref.nim @@ -14,7 +14,7 @@ type processes {.guard: lock.}: array[0..MAX_WORKERS-1, foreign ptr Process] # Hold a lock for a statement. -template hold(lock: Lock, body: stmt) = +template hold(lock: Lock, body: untyped) = lock.acquire defer: lock.release {.locks: [lock].}: diff --git a/tests/parser/toprprec.nim b/tests/parser/toprprec.nim index 2c22f5b80..1acd381e7 100644 --- a/tests/parser/toprprec.nim +++ b/tests/parser/toprprec.nim @@ -4,9 +4,11 @@ discard """ """ # Test operator precedence: -template `@` (x: expr): expr {.immediate.} = self.x -template `@!` (x: expr): expr {.immediate.} = x -template `===` (x: expr): expr {.immediate.} = x +template `@` (x: untyped): untyped {.immediate.} = + `self`.x + +template `@!` (x: untyped): untyped = x +template `===` (x: untyped): untyped = x type TO = object @@ -34,6 +36,3 @@ var s: TA assert init(s) == "4" echo "done" - - - diff --git a/tests/parser/tpostexprblocks.nim b/tests/parser/tpostexprblocks.nim index 85f2628bf..341ca737a 100644 --- a/tests/parser/tpostexprblocks.nim +++ b/tests/parser/tpostexprblocks.nim @@ -1,51 +1,59 @@ discard """ nimout: ''' StmtList - Ident !"foo" + Ident !"foo010" Call - Ident !"foo" + Ident !"foo020" Call - Ident !"foo" + Ident !"foo030" Ident !"x" Command - Ident !"foo" + Ident !"foo040" Ident !"x" Call - Ident !"foo" + Ident !"foo050" StmtList DiscardStmt Empty Call - Ident !"foo" + Ident !"foo060" StmtList DiscardStmt Empty Call - Ident !"foo" + Ident !"foo070" StrLit test StmtList DiscardStmt Empty Call - Ident !"foo" + Ident !"foo080" StrLit test StmtList DiscardStmt Empty Command - Ident !"foo" + Ident !"foo090" StrLit test StmtList DiscardStmt Empty Command - Ident !"foo" - StrLit test - StmtList - DiscardStmt - Empty + Ident !"foo100" + Call + StrLit test + StmtList + DiscardStmt + Empty Command - Ident !"foo" + Ident !"foo101" + Call + IntLit 10 + StmtList + DiscardStmt + Empty + Command + Ident !"foo110" IntLit 1 Par Infix @@ -56,18 +64,19 @@ StmtList DiscardStmt Empty Command - Ident !"foo" + Ident !"foo120" IntLit 1 - Par - Infix - Ident !"+" - IntLit 2 - IntLit 3 - StmtList - DiscardStmt - Empty + Call + Par + Infix + Ident !"+" + IntLit 2 + IntLit 3 + StmtList + DiscardStmt + Empty Call - Ident !"foo" + Ident !"foo130" Do Empty Empty @@ -84,7 +93,7 @@ StmtList DiscardStmt Empty Call - Ident !"foo" + Ident !"foo140" Do Empty Empty @@ -101,7 +110,7 @@ StmtList DiscardStmt Empty Call - Ident !"foo" + Ident !"foo150" Do Empty Empty @@ -118,25 +127,26 @@ StmtList DiscardStmt Empty Command - Ident !"foo" - Ident !"x" - Do - Empty - Empty - Empty - FormalParams + Ident !"foo160" + Call + Ident !"x" + Do Empty - IdentDefs - Ident !"y" - Empty - Empty - Empty - Empty - StmtList - DiscardStmt + Empty + Empty + FormalParams Empty + IdentDefs + Ident !"y" + Empty + Empty + Empty + Empty + StmtList + DiscardStmt + Empty Call - Ident !"foo" + Ident !"foo170" StmtList DiscardStmt Empty @@ -145,7 +155,7 @@ StmtList DiscardStmt Empty Call - Ident !"foo" + Ident !"foo180" StmtList DiscardStmt Empty @@ -157,62 +167,63 @@ StmtList DiscardStmt Empty Command - Ident !"foo" - Ident !"x" - Do - Empty - Empty - Empty - FormalParams + Ident !"foo190" + Call + Ident !"x" + Do Empty - IdentDefs - Ident !"y" - Empty - Empty - Empty - Empty - StmtList - DiscardStmt - Empty - Do - Empty - Empty - Empty - FormalParams - Ident !"int" - IdentDefs - Ident !"z" - Empty - Empty - Empty - Empty - StmtList - DiscardStmt + Empty + Empty + FormalParams Empty - Do - Empty - Empty - Empty - FormalParams - Ident !"int" - IdentDefs - Ident !"w" + IdentDefs + Ident !"y" + Empty + Empty + Empty + Empty + StmtList + DiscardStmt + Empty + Do + Empty + Empty + Empty + FormalParams Ident !"int" - Empty - Empty - Empty - StmtList - DiscardStmt - Empty - StmtList - DiscardStmt + IdentDefs + Ident !"z" + Empty + Empty Empty - Else + Empty + StmtList + DiscardStmt + Empty + Do + Empty + Empty + Empty + FormalParams + Ident !"int" + IdentDefs + Ident !"w" + Ident !"int" + Empty + Empty + Empty + StmtList + DiscardStmt + Empty StmtList DiscardStmt Empty + Else + StmtList + DiscardStmt + Empty Call - Ident !"foo" + Ident !"foo200" Ident !"x" Call Ident !"bar" @@ -227,33 +238,33 @@ StmtList IdentDefs Ident !"a" Empty - Ident !"foo" + Ident !"foo210" VarSection IdentDefs Ident !"a" Empty Call - Ident !"foo" + Ident !"foo220" VarSection IdentDefs Ident !"a" Empty Call - Ident !"foo" + Ident !"foo230" Ident !"x" VarSection IdentDefs Ident !"a" Empty Command - Ident !"foo" + Ident !"foo240" Ident !"x" VarSection IdentDefs Ident !"a" Empty Call - Ident !"foo" + Ident !"foo250" StmtList DiscardStmt Empty @@ -262,7 +273,7 @@ StmtList Ident !"a" Empty Call - Ident !"foo" + Ident !"foo260" StmtList DiscardStmt Empty @@ -271,7 +282,7 @@ StmtList Ident !"a" Empty Call - Ident !"foo" + Ident !"foo270" StmtList DiscardStmt Empty @@ -284,62 +295,63 @@ StmtList Ident !"a" Empty Command - Ident !"foo" - Ident !"x" - Do - Empty - Empty - Empty - FormalParams + Ident !"foo280" + Call + Ident !"x" + Do Empty - IdentDefs - Ident !"y" - Empty - Empty - Empty - Empty - StmtList - DiscardStmt - Empty - Else - StmtList - DiscardStmt + Empty + Empty + FormalParams Empty + IdentDefs + Ident !"y" + Empty + Empty + Empty + Empty + StmtList + DiscardStmt + Empty + Else + StmtList + DiscardStmt + Empty Asgn Ident !"a" - Ident !"foo" + Ident !"foo290" Asgn Ident !"a" Call - Ident !"foo" + Ident !"foo300" Asgn Ident !"a" Call - Ident !"foo" + Ident !"foo310" Ident !"x" Asgn Ident !"a" Command - Ident !"foo" + Ident !"foo320" Ident !"x" Asgn Ident !"a" Call - Ident !"foo" + Ident !"foo330" StmtList DiscardStmt Empty Asgn Ident !"a" Call - Ident !"foo" + Ident !"foo340" StmtList DiscardStmt Empty Asgn Ident !"a" Call - Ident !"foo" + Ident !"foo350" StmtList DiscardStmt Empty @@ -350,30 +362,42 @@ StmtList Asgn Ident !"a" Command - Ident !"foo" - Ident !"x" - Do - Empty - Empty - Empty - FormalParams + Ident !"foo360" + Call + DotExpr + Ident !"x" + Ident !"bar" + Do Empty - IdentDefs - Ident !"y" - Empty - Empty - Empty - Empty - StmtList - DiscardStmt - Empty - Else - StmtList - DiscardStmt + Empty + Empty + FormalParams Empty + IdentDefs + Ident !"y" + Empty + Empty + Empty + Empty + StmtList + DiscardStmt + Empty + Else + StmtList + DiscardStmt + Empty + Command + DotExpr + Ident !"foo370" + Ident !"add" + Call + Ident !"quote" + StmtList + DiscardStmt + Empty Call DotExpr - Ident !"result" + Ident !"foo380" Ident !"add" BracketExpr Call @@ -389,62 +413,65 @@ import macros dumpTree: # simple calls - foo - foo() - foo(x) - foo x + foo010 + foo020() + foo030(x) + foo040 x + + foo050: + discard - foo: + foo060 do: discard - foo do: + foo070("test"): discard - foo("test"): + foo080("test") do: discard - foo("test") do: + foo090 "test": discard - foo "test": + foo100 "test" do: discard - foo "test" do: + foo101 10 do: discard # more complicated calls - foo 1, (2+3): + foo110 1, (2+3): discard - foo 1, (2+3) do: + foo120 1, (2+3) do: discard - foo do (x): + foo130 do (x): discard - foo do (x: int): + foo140 do (x: int): discard - foo do (x: int) -> int: + foo150 do (x: int) -> int: discard - foo x do (y): + foo160 x do (y): discard # extra blocks - foo: + foo170: discard else: discard - foo do: + foo180 do: discard do: discard else: discard - foo x do (y): + foo190 x do (y): discard do (z) -> int: discard @@ -456,58 +483,61 @@ dumpTree: discard # call with blocks as a param - foo(x, bar do: + foo200(x, bar do: discard else: discard ) # introduce a variable - var a = foo - var a = foo() - var a = foo(x) - var a = foo x + var a = foo210 + var a = foo220() + var a = foo230(x) + var a = foo240 x - var a = foo: + var a = foo250: discard - var a = foo do: + var a = foo260 do: discard - var a = foo do: + var a = foo270 do: discard else: discard - var a = foo x do (y): + var a = foo280 x do (y): discard else: discard # assignments - a = foo - a = foo() - a = foo(x) - a = foo x + a = foo290 + a = foo300() + a = foo310(x) + a = foo320 x - a = foo: + a = foo330: discard - a = foo do: + a = foo340 do: discard - a = foo do: + a = foo350 do: discard else: discard - a = foo x do (y): + a = foo360 x.bar do (y): discard else: discard + foo370.add quote do: + discard + # some edge cases - result.add((quote do: + foo380.add((quote do: discard )[0]) diff --git a/tests/parser/tstrongspaces.nim b/tests/parser/tstrongspaces.nim index cb0219976..adab7f709 100644 --- a/tests/parser/tstrongspaces.nim +++ b/tests/parser/tstrongspaces.nim @@ -40,9 +40,9 @@ echo $foo echo (1, 2, 2) -template `&`(a, b: int): expr = a and b -template `|`(a, b: int): expr = a - b -template `++`(a, b: int): expr = a + b == 8009 +template `&`(a, b: int): int = a and b +template `|`(a, b: int): int = a - b +template `++`(a, b: int): bool = a + b == 8009 when true: let b = 66 @@ -62,7 +62,7 @@ when true: echo booA == booB -template `|`(a, b): expr = (if a.len > 0: a else: b) +template `|`(a, b): untyped = (if a.len > 0: a else: b) const tester = "tester" @@ -74,7 +74,7 @@ echo "all"|tester & " " & args # Test arrow like operators. See also tests/macros/tclosuremacro.nim proc `+->`(a, b: int): int = a + b*4 -template `===>`(a, b: int): expr = a - b shr 1 +template `===>`(a, b: int): int = a - b shr 1 echo 3 +-> 2 + 2 and 4 var arrowed = 3+->2 + 2 and 4 # arrowed = 4 diff --git a/tests/pragmas/tsym_as_pragma.nim b/tests/pragmas/tsym_as_pragma.nim index f6ba44dc9..788311244 100644 --- a/tests/pragmas/tsym_as_pragma.nim +++ b/tests/pragmas/tsym_as_pragma.nim @@ -1,7 +1,7 @@ # bug #3171 -template newDataWindow(): stmt = +template newDataWindow(): untyped = let eventClosure = proc (closure: pointer): bool {.closure, cdecl.} = discard diff --git a/tests/pragmas/tused.nim b/tests/pragmas/tused.nim index 389863aef..83c62b7bb 100644 --- a/tests/pragmas/tused.nim +++ b/tests/pragmas/tused.nim @@ -1,7 +1,7 @@ discard """ nimout: ''' compile start -tused.nim(15, 8) Hint: 'tused.echoSub(a: int, b: int)[declared in tused.nim(15,7)]' is declared but not used [XDeclaredButNotUsed] +tused.nim(15, 8) Hint: 'tused.echoSub(a: int, b: int)[declared in tused.nim(15, 7)]' is declared but not used [XDeclaredButNotUsed] compile end''' output: "8\n8" """ diff --git a/tests/range/compilehelpers.nim b/tests/range/compilehelpers.nim index cb26ca5b5..08f97a5bf 100644 --- a/tests/range/compilehelpers.nim +++ b/tests/range/compilehelpers.nim @@ -1,6 +1,5 @@ -template accept(e: expr) = +template accept(e) = static: assert(compiles(e)) -template reject(e: expr) = +template reject(e) = static: assert(not compiles(e)) - diff --git a/tests/readme.md b/tests/readme.md new file mode 100644 index 000000000..34a2c4bfb --- /dev/null +++ b/tests/readme.md @@ -0,0 +1,62 @@ +This directory contains the test cases. + +Each test must have a filename of the form: ``t*.nim`` + +**Note:** Tests are only compiled by default. In order to get the tester to +execute the compiled binary, you need to specify a spec with an ``action`` key +(see below for details). + +# Specs + +Each test can contain a spec in a ``discard """ ... """`` block. + +**Check out the [``parseSpec`` procedure](https://github.com/nim-lang/Nim/blob/devel/tests/testament/specs.nim#L124) in the ``specs`` module for a full and reliable reference** + +## action + +Specifies what action this test should take. + +**Default: compile** + +Options: + +* ``compile`` - compiles the module and fails the test if compilations fails. +* ``run`` - compiles and runs the module, fails the test if compilation or + execution of test code fails. +* ``reject`` - compiles the module and fails the test if compilation succeeds. + +There are certain spec keys that imply ``run``, including ``output`` and +``outputsub``. + +## cmd + +Specifies the Nim command to use for compiling the test. + +There are a number of variables that are replaced in this spec option: + +* ``$target`` - the compilation target, e.g. ``c``. +* ``$options`` - the options for the compiler. +* ``$file`` - the filename of the test. +* ``$filedir`` - the directory of the test file. + +Example: + +```nim +discard """ + cmd: "nim $target --nimblePath:./nimbleDir/simplePkgs $options $file" +""" +``` + +# Categories + +Each folder under this directory represents a test category, which can be +tested by running `koch tests cat <category>`. + +The folder ``rodfiles`` contains special tests that test incremental +compilation via symbol files. + +The folder ``dll`` contains simple DLL tests. + +The folder ``realtimeGC`` contains a test for validating that the realtime GC +can run properly without linking against the nimrtl.dll/so. It includes a C +client and platform specific build files for manual compilation. diff --git a/tests/readme.txt b/tests/readme.txt deleted file mode 100644 index 0ff9e11c6..000000000 --- a/tests/readme.txt +++ /dev/null @@ -1,13 +0,0 @@ -This directory contains the test cases. -Each test must have a filename of the form: ``t*.nim`` - -Each test can contain a spec in a ``discard """"""`` block. - -The folder ``rodfiles`` contains special tests that test incremental -compilation via symbol files. - -The folder ``dll`` contains simple DLL tests. - -The folder ``realtimeGC`` contains a test for validating that the realtime GC -can run properly without linking against the nimrtl.dll/so. It includes a C -client and platform specific build files for manual compilation. diff --git a/tests/showoff/tdrdobbs_examples.nim b/tests/showoff/tdrdobbs_examples.nim index 78f711325..8687ee529 100644 --- a/tests/showoff/tdrdobbs_examples.nim +++ b/tests/showoff/tdrdobbs_examples.nim @@ -105,10 +105,10 @@ proc pat2kind(pattern: string): FormulaKind = import macros proc matchAgainst(n, pattern: NimNode): NimNode {.compileTime.} = - template `@`(current, field: expr): expr = + template `@`(current, field: untyped): untyped = newDotExpr(current, newIdentNode(astToStr(field))) - template `==@`(n, pattern: expr): expr = + template `==@`(n, pattern: untyped): untyped = newCall("==", n@kind, newIdentNode($pat2kind($pattern.ident))) case pattern.kind @@ -126,7 +126,7 @@ proc matchAgainst(n, pattern: NimNode): NimNode {.compileTime.} = else: error "invalid pattern" -macro `=~` (n: Formula, pattern: expr): bool = +macro `=~` (n: Formula, pattern: untyped): bool = result = matchAgainst(n, pattern) proc isPolyTerm2(n: Formula): bool = n =~ c * x^c diff --git a/tests/showoff/tformatopt.nim b/tests/showoff/tformatopt.nim index f33ed6921..6e790c38e 100644 --- a/tests/showoff/tformatopt.nim +++ b/tests/showoff/tformatopt.nim @@ -10,7 +10,7 @@ import macros proc invalidFormatString() = echo "invalidFormatString" -template formatImpl(handleChar: expr) = +template formatImpl(handleChar: untyped) = var i = 0 while i < f.len: if f[i] == '$': @@ -29,11 +29,11 @@ template formatImpl(handleChar: expr) = i += 1 proc `%`*(f: string, a: openArray[string]): string = - template identity(x: expr): expr = x + template identity(x: untyped): untyped = x result = "" formatImpl(identity) -macro optFormat{`%`(f, a)}(f: string{lit}, a: openArray[string]): expr = +macro optFormat{`%`(f, a)}(f: string{lit}, a: openArray[string]): untyped = result = newNimNode(nnkBracket) let f = f.strVal formatImpl(newLit) diff --git a/tests/showoff/thtml1.nim b/tests/showoff/thtml1.nim index c7a083c21..fe0cd3b1e 100644 --- a/tests/showoff/thtml1.nim +++ b/tests/showoff/thtml1.nim @@ -2,7 +2,7 @@ discard """ output: "<br>" """ -template htmlTag(tag: expr) {.immediate.} = +template htmlTag(tag: untyped) = proc tag(): string = "<" & astToStr(tag) & ">" htmlTag(br) diff --git a/tests/showoff/thtml2.nim b/tests/showoff/thtml2.nim index faeb4e50d..dcf6534a5 100644 --- a/tests/showoff/thtml2.nim +++ b/tests/showoff/thtml2.nim @@ -4,20 +4,20 @@ discard """ import strutils -template html(name: expr, matter: stmt) {.immediate.} = +template html(name, matter: untyped) = proc name(): string = result = "<html>" matter result.add("</html>") -template nestedTag(tag: expr) {.immediate.} = - template tag(matter: stmt) {.immediate.} = +template nestedTag(tag: untyped) = + template tag(matter: untyped) = result.add("<" & astToStr(tag) & ">") matter result.add("</" & astToStr(tag) & ">") -template simpleTag(tag: expr) {.immediate.} = - template tag(matter: expr) {.immediate.} = +template simpleTag(tag: untyped) = + template tag(matter: untyped) = result.add("<$1>$2</$1>" % [astToStr(tag), matter]) nestedTag body diff --git a/tests/showoff/tonce.nim b/tests/showoff/tonce.nim index 6fc372e87..ed2684dcf 100644 --- a/tests/showoff/tonce.nim +++ b/tests/showoff/tonce.nim @@ -5,7 +5,7 @@ new instantiation some call of p''' """ -template once(body: stmt) = +template once(body) = var x {.global.} = false if not x: x = true diff --git a/tests/showoff/tquasiquote.nim b/tests/showoff/tquasiquote.nim index df7fccc33..404712a02 100644 --- a/tests/showoff/tquasiquote.nim +++ b/tests/showoff/tquasiquote.nim @@ -1,10 +1,10 @@ discard """ - outputsub: '''tquasiquote.nim(14,8): Check failed: 1 > 2''' + outputsub: '''tquasiquote.nim(14, 8): Check failed: 1 > 2''' """ import macros -macro check(ex: expr): stmt = +macro check(ex: untyped): untyped = var info = ex.lineInfo var expString = ex.toStrLit result = quote do: diff --git a/tests/specialops/tdotops.nim b/tests/specialops/tdotops.nim index ce5b3942d..bca949922 100644 --- a/tests/specialops/tdotops.nim +++ b/tests/specialops/tdotops.nim @@ -31,7 +31,7 @@ proc `.=`(x: var T1, f: string{lit}, v: int) = echo "assigning ", f, " = ", v x.x = v -template `.()`(x: T1, f: string, args: varargs[expr]): string = +template `.()`(x: T1, f: string, args: varargs[typed]): string = echo "call to ", f "dot call" @@ -63,4 +63,3 @@ echo tt.c(10) assert(not compiles(tt.d("x"))) assert(not compiles(tt.d(1, 2))) - diff --git a/tests/stdlib/tlocks.nim b/tests/stdlib/tlocks.nim new file mode 100644 index 000000000..e567cfde8 --- /dev/null +++ b/tests/stdlib/tlocks.nim @@ -0,0 +1,10 @@ +discard """ + output: '''3''' + cmd: "nim $target --threads:on $options $file" +""" + +#bug #6049 +import uselocks + +var m = createMyType[int]() +echo $m.use() diff --git a/tests/stdlib/tmarshal.nim b/tests/stdlib/tmarshal.nim index 6a53a2964..434caa281 100644 --- a/tests/stdlib/tmarshal.nim +++ b/tests/stdlib/tmarshal.nim @@ -9,7 +9,7 @@ omega 200 import marshal -template testit(x: expr) = discard $$to[type(x)]($$x) +template testit(x) = discard $$to[type(x)]($$x) var x: array[0..4, array[0..4, string]] = [ ["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"], diff --git a/tests/stdlib/tnre.nim b/tests/stdlib/tnre.nim index 030319ebf..fabbb69a8 100644 --- a/tests/stdlib/tnre.nim +++ b/tests/stdlib/tnre.nim @@ -20,12 +20,13 @@ discard """ [Suite] Misc tests''' """ + import nre -import nre.init -import nre.captures -import nre.find -import nre.split -import nre.match -import nre.replace -import nre.escape -import nre.misc +import nre/init +import nre/captures +import nre/find +import nre/split +import nre/match +import nre/replace +import nre/escape +import nre/misc diff --git a/tests/stdlib/tparsecfg.nim b/tests/stdlib/tparsecfg.nim new file mode 100644 index 000000000..1c214e5d2 --- /dev/null +++ b/tests/stdlib/tparsecfg.nim @@ -0,0 +1,23 @@ +discard """ + output: '''OK''' +""" + +#bug #6046 +import parsecfg + +var config = newConfig() +config.setSectionKey("foo","bar","-1") +config.setSectionKey("foo","foo","abc") +config.writeConfig("test.ini") + +# test.ini now contains +# [foo] +# bar=-1 +# foo=abc + +var config2 = loadConfig("test.ini") +let bar = config2.getSectionValue("foo","bar") +let foo = config2.getSectionValue("foo","foo") +assert(bar == "-1") +assert(foo == "abc") +echo "OK" diff --git a/tests/stdlib/tpegs.nim b/tests/stdlib/tpegs.nim index ec839e288..e2a5a1715 100644 --- a/tests/stdlib/tpegs.nim +++ b/tests/stdlib/tpegs.nim @@ -149,7 +149,7 @@ proc addChoice(dest: var TPeg, elem: TPeg) = else: add(dest, elem) else: add(dest, elem) -template multipleOp(k: TPegKind, localOpt: expr) = +template multipleOp(k: TPegKind, localOpt) = result.kind = k result.sons = @[] for x in items(a): @@ -350,32 +350,32 @@ proc newNonTerminal*(name: string, line, column: int): PNonTerminal {. result.line = line result.col = column -template letters*: expr = +template letters*: TPeg = ## expands to ``charset({'A'..'Z', 'a'..'z'})`` charset({'A'..'Z', 'a'..'z'}) -template digits*: expr = +template digits*: TPeg = ## expands to ``charset({'0'..'9'})`` charset({'0'..'9'}) -template whitespace*: expr = +template whitespace*: TPeg = ## expands to ``charset({' ', '\9'..'\13'})`` charset({' ', '\9'..'\13'}) -template identChars*: expr = +template identChars*: TPeg = ## expands to ``charset({'a'..'z', 'A'..'Z', '0'..'9', '_'})`` charset({'a'..'z', 'A'..'Z', '0'..'9', '_'}) -template identStartChars*: expr = +template identStartChars*: TPeg = ## expands to ``charset({'A'..'Z', 'a'..'z', '_'})`` charset({'a'..'z', 'A'..'Z', '_'}) -template ident*: expr = +template ident*: TPeg = ## same as ``[a-zA-Z_][a-zA-z_0-9]*``; standard identifier sequence(charset({'a'..'z', 'A'..'Z', '_'}), *charset({'a'..'z', 'A'..'Z', '0'..'9', '_'})) -template natural*: expr = +template natural*: TPeg = ## same as ``\d+`` +digits @@ -534,10 +534,10 @@ proc bounds*(c: TCaptures, when not useUnicode: type Rune = char - template fastRuneAt(s, i, ch: expr) = + template fastRuneAt(s, i, ch) = ch = s[i] inc(i) - template runeLenAt(s, i: expr): expr = 1 + template runeLenAt(s, i): untyped = 1 proc isAlpha(a: char): bool {.inline.} = return a in {'a'..'z','A'..'Z'} proc isUpper(a: char): bool {.inline.} = return a in {'A'..'Z'} @@ -847,7 +847,7 @@ proc findAll*(s: string, pattern: TPeg, start = 0): seq[string] {. ## If it does not match, @[] is returned. accumulateResult(findAll(s, pattern, start)) -template `=~`*(s: string, pattern: TPeg): expr = +template `=~`*(s: string, pattern: TPeg): untyped = ## This calls ``match`` with an implicit declared ``matches`` array that ## can be used in the scope of the ``=~`` call: ## diff --git a/tests/stdlib/uselocks.nim b/tests/stdlib/uselocks.nim new file mode 100644 index 000000000..cde9641b2 --- /dev/null +++ b/tests/stdlib/uselocks.nim @@ -0,0 +1,11 @@ +import locks + +type MyType* [T] = object + lock: Lock + +proc createMyType*[T]: MyType[T] = + initLock(result.lock) + +proc use* (m: var MyType): int = + withLock m.lock: + result = 3 diff --git a/tests/template/annotate.nim b/tests/template/annotate.nim index 5f395557b..a7e2f8fdb 100644 --- a/tests/template/annotate.nim +++ b/tests/template/annotate.nim @@ -1,7 +1,7 @@ import macros, parseutils # Generate tags -macro make(names: openarray[expr]): stmt {.immediate.} = +macro make(names: untyped{nkBracket}): untyped = result = newStmtList() for i in 0 .. names.len-1: diff --git a/tests/template/mcan_access_hidden_field.nim b/tests/template/mcan_access_hidden_field.nim index bf3592701..2c0026ec1 100644 --- a/tests/template/mcan_access_hidden_field.nim +++ b/tests/template/mcan_access_hidden_field.nim @@ -5,5 +5,4 @@ type proc createFoo*(a, b: int): Foo = Foo(fooa: a, foob: b) -template geta*(f: Foo): expr = f.fooa - +template geta*(f: Foo): untyped = f.fooa diff --git a/tests/template/t2do.nim b/tests/template/t2do.nim index ec364c5f3..f5f6393dc 100644 --- a/tests/template/t2do.nim +++ b/tests/template/t2do.nim @@ -7,7 +7,7 @@ discard """ proc mpf_get_d(x: int): float = float(x) proc mpf_cmp_d(a: int; b: float): int = 0 -template toFloatHelper(result: expr; tooSmall, tooLarge: stmt) {.immediate.} = +template toFloatHelper(result, tooSmall, tooLarge: untyped) = result = mpf_get_d(a) if result == 0.0 and mpf_cmp_d(a,0.0) != 0: tooSmall diff --git a/tests/template/t_otemplates.nim b/tests/template/t_otemplates.nim index 6c419f72f..6597bd37a 100644 --- a/tests/template/t_otemplates.nim +++ b/tests/template/t_otemplates.nim @@ -126,7 +126,7 @@ iterator parse_compound_statements(value, identifier: string, index: int): strin ## and returns the initialization of each as an empty statement ## i.e. if x == 5 { ... } becomes if x == 5: nil. - template get_next_ident(expected): stmt = + template get_next_ident(expected) = var nextIdent: string discard value.parseWhile(nextIdent, {'$'} + identChars, i) @@ -316,10 +316,10 @@ proc parse_template(node: NimNode, value: string) = ## Nim code into the input `node` AST. var index = 0 while index < value.len and - parse_until_symbol(node, value, index): nil + parse_until_symbol(node, value, index): discard -macro tmpli*(body: expr): stmt = +macro tmpli*(body: untyped): untyped = result = newStmtList() result.add parseExpr("result = \"\"") @@ -330,7 +330,7 @@ macro tmpli*(body: expr): stmt = parse_template(result, reindent(value)) -macro tmpl*(body: expr): stmt = +macro tmpl*(body: untyped): untyped = result = newStmtList() var value = if body.kind in nnkStrLit..nnkTripleStrLit: body.strVal diff --git a/tests/template/tdefault_nil.nim b/tests/template/tdefault_nil.nim index 891166306..c5c372d9e 100644 --- a/tests/template/tdefault_nil.nim +++ b/tests/template/tdefault_nil.nim @@ -2,7 +2,7 @@ # bug #2629 import sequtils, os -template glob_rst(basedir: string = nil): expr = +template glob_rst(basedir: string = nil): untyped = if baseDir.isNil: to_seq(walk_files("*.rst")) else: diff --git a/tests/template/thygienictempl.nim b/tests/template/thygienictempl.nim index 5e4f534f8..de40450aa 100644 --- a/tests/template/thygienictempl.nim +++ b/tests/template/thygienictempl.nim @@ -2,7 +2,7 @@ var e = "abc" -raise newException(EIO, e & "ha!") +raise newException(IOError, e & "ha!") template t() = echo(foo) @@ -10,7 +10,7 @@ var foo = 12 t() -template test_in(a, b, c: expr): bool {.immediate, dirty.} = +template test_in(a, b, c: untyped): bool {.dirty.} = var result {.gensym.}: bool = false false diff --git a/tests/template/tissue909.nim b/tests/template/tissue909.nim index 5b57a3558..6786ff48c 100644 --- a/tests/template/tissue909.nim +++ b/tests/template/tissue909.nim @@ -8,7 +8,7 @@ template baz() = var y = foo discard y() -macro test(): stmt = +macro test(): untyped = result = getAst(baz()) echo(treeRepr(result)) diff --git a/tests/template/tissue993.nim b/tests/template/tissue993.nim index dae9df683..552890bb4 100644 --- a/tests/template/tissue993.nim +++ b/tests/template/tissue993.nim @@ -1,15 +1,15 @@ type PNode* = ref object of RootObj -template litNode (name, ty): stmt = +template litNode(name, ty) = type name* = ref object of PNode val*: ty litNode PIntNode, int import json -template withKey*(j: JsonNode; key: string; varname: expr; - body:stmt): stmt {.immediate.} = +template withKey*(j: JsonNode; key: string; varname, + body: untyped): typed = if j.hasKey(key): let varname{.inject.}= j[key] block: @@ -18,4 +18,3 @@ template withKey*(j: JsonNode; key: string; varname: expr; var j = parsejson("{\"zzz\":1}") withkey(j, "foo", x): echo(x) - diff --git a/tests/template/tit.nim b/tests/template/tit.nim index cf50d2f6f..76b1d151b 100644 --- a/tests/template/tit.nim +++ b/tests/template/tit.nim @@ -1,7 +1,7 @@ # bug #1337 -template someIt(a, pred: expr): expr = +template someIt(a, pred): untyped = var it {.inject.} = 0 pred diff --git a/tests/template/tparams_gensymed.nim b/tests/template/tparams_gensymed.nim index 568725fd4..3fb0dd4a5 100644 --- a/tests/template/tparams_gensymed.nim +++ b/tests/template/tparams_gensymed.nim @@ -43,7 +43,7 @@ template forStatic(index, slice, predicate: untyped) = block: const index = i predicate - template iterateStartingFrom(i: int): stmt = + template iterateStartingFrom(i: int) = when i <= b: iteration i iterateStartingFrom i + 1 diff --git a/tests/template/tprefer_immediate.nim b/tests/template/tprefer_immediate.nim index 578f447b0..3a4cfc07b 100644 --- a/tests/template/tprefer_immediate.nim +++ b/tests/template/tprefer_immediate.nim @@ -4,14 +4,12 @@ discard """ # Test that immediate templates are preferred over non-immediate templates -template foo(a, b: expr) = echo "foo expr" - +template foo(a, b: untyped) = echo "foo expr" template foo(a, b: int) = echo "foo int" template foo(a, b: float) = echo "foo float" template foo(a, b: string) = echo "foo string" -template foo(a, b: expr) {.immediate.} = echo "immediate" +template foo(a, b: untyped) {.immediate.} = echo "immediate" template foo(a, b: bool) = echo "foo bool" template foo(a, b: char) = echo "foo char" foo(undeclaredIdentifier, undeclaredIdentifier2) - diff --git a/tests/template/tscope.nim b/tests/template/tscope.nim index 2d5841af3..1eeebbdd4 100644 --- a/tests/template/tscope.nim +++ b/tests/template/tscope.nim @@ -3,7 +3,7 @@ discard """ """ var x = 1 -template quantity(): stmt {.immediate.} = +template quantity() = # Causes internal error in compiler/sem.nim proc unit*(x = 1.0): float = 12 # Throws the correct error: redefinition of 'x' diff --git a/tests/template/tstmt_semchecked_twice.nim b/tests/template/tstmt_semchecked_twice.nim index 05c16c3c9..c6463ae06 100644 --- a/tests/template/tstmt_semchecked_twice.nim +++ b/tests/template/tstmt_semchecked_twice.nim @@ -13,7 +13,7 @@ type Vector2[T] = T Pixels=int -template use*(fb: int, st: stmt) : stmt = +template use*(fb: int, st: untyped): untyped = echo "a ", $fb st echo "a ", $fb diff --git a/tests/template/tsymchoicefield.nim b/tests/template/tsymchoicefield.nim index ab05500bf..4483c2aa2 100644 --- a/tests/template/tsymchoicefield.nim +++ b/tests/template/tsymchoicefield.nim @@ -3,10 +3,9 @@ type Foo = object var f = Foo(len: 40) -template getLen(f: Foo): expr = f.len +template getLen(f: Foo): int = f.len echo f.getLen # This fails, because `len` gets the nkOpenSymChoice # treatment inside the template early pass and then # it can't be recognized as a field anymore - diff --git a/tests/template/ttempl3.nim b/tests/template/ttempl3.nim index 56daf9fe6..91d416c48 100644 --- a/tests/template/ttempl3.nim +++ b/tests/template/ttempl3.nim @@ -1,6 +1,6 @@ -template withOpenFile(f: expr, filename: string, mode: TFileMode, - actions: stmt): stmt {.immediate.} = +template withOpenFile(f: untyped, filename: string, mode: TFileMode, + actions: untyped): untyped = block: # test that 'f' is implicitly 'injecting': var f: TFile @@ -20,20 +20,20 @@ var myVar: array[0..1, int] # Test zero argument template: -template ha: expr = myVar[0] +template ha: untyped = myVar[0] ha = 1 echo(ha) # Test identifier generation: -template prefix(name: expr): expr {.immediate.} = `"hu" name` +template prefix(name): untyped = `"hu" name` var `hu "XYZ"` = "yay" echo prefix(XYZ) -template typedef(name: expr, typ: typeDesc) {.immediate, dirty.} = +template typedef(name: untyped, typ: typeDesc) {.immediate, dirty.} = type `T name`* = typ `P name`* = ref `T name` @@ -51,7 +51,7 @@ type proc initFoo(arg: int): Foo = result.arg = arg -template create(typ: typeDesc, arg: expr): expr = `init typ`(arg) +template create(typ: typeDesc, arg: untyped): untyped = `init typ`(arg) var ff = Foo.create(12) diff --git a/tests/template/ttempl4.nim b/tests/template/ttempl4.nim index 26c82e471..d1d26385f 100644 --- a/tests/template/ttempl4.nim +++ b/tests/template/ttempl4.nim @@ -1,8 +1,7 @@ -template `:=`(name, val: expr): stmt {.immediate.} = +template `:=`(name, val: untyped): typed = var name = val ha := 1 * 4 hu := "ta-da" == "ta-da" echo ha, hu - diff --git a/tests/template/ttempl5.nim b/tests/template/ttempl5.nim index a020a8e2c..fd3ea0cad 100644 --- a/tests/template/ttempl5.nim +++ b/tests/template/ttempl5.nim @@ -9,7 +9,7 @@ proc parse_to_close(value: string, index: int, open='(', close=')'): int = discard # Call parse_to_close -template get_next_ident: stmt = +template get_next_ident: typed = discard "{something}".parse_to_close(0, open = '{', close = '}') get_next_ident() @@ -19,11 +19,10 @@ get_next_ident() #bug #880 (also example in the manual!) -template typedef(name: expr, typ: typedesc) {.immediate.} = +template typedef(name: untyped, typ: typedesc) = type `T name`* {.inject.} = typ `P name`* {.inject.} = ref `T name` typedef(myint, int) var x: PMyInt - diff --git a/tests/template/twhen_gensym.nim b/tests/template/twhen_gensym.nim index d84ee6f03..f1a8d0eb7 100644 --- a/tests/template/twhen_gensym.nim +++ b/tests/template/twhen_gensym.nim @@ -3,7 +3,7 @@ discard """ """ # bug #2670 -template testTemplate(b: bool): stmt = +template testTemplate(b: bool): typed = when b: var a = "hi" else: diff --git a/tests/template/twrongmapit.nim b/tests/template/twrongmapit.nim index df695fcd6..cbc63b9cd 100644 --- a/tests/template/twrongmapit.nim +++ b/tests/template/twrongmapit.nim @@ -8,7 +8,7 @@ discard """ type Foo* {.pure, final.} = object elt: float -template defineOpAssign(T: expr, op: expr) {.immediate.} = +template defineOpAssign(T, op: untyped) {.immediate.} = proc op*(v: var T, w: T) {.inline.} = for i in 0..1: op(v.elt, w.elt) @@ -18,7 +18,7 @@ const ATTEMPT = 0 when ATTEMPT == 0: # FAILS: defining `/=` with template calling template # ERROR about sem.nim line 144 - template defineOpAssigns(T: expr) {.immediate.} = + template defineOpAssigns(T: untyped) = mixin `/=` defineOpAssign(T, `/=`) diff --git a/tests/template/twrongsymkind.nim b/tests/template/twrongsymkind.nim index be3d8c652..5fa618914 100644 --- a/tests/template/twrongsymkind.nim +++ b/tests/template/twrongsymkind.nim @@ -9,7 +9,7 @@ type MyData = object x: int -template newDataWindow(data: ref MyData): stmt = +template newDataWindow(data: ref MyData): untyped = proc testProc(data: ref MyData) = echo "Hello, ", data.x testProc(data) diff --git a/tests/template/utemplates.nim b/tests/template/utemplates.nim index 8b9ae5d26..199268046 100644 --- a/tests/template/utemplates.nim +++ b/tests/template/utemplates.nim @@ -1,32 +1,31 @@ import unittest -template t(a: int): expr = "int" -template t(a: string): expr = "string" +template t(a: int): string = "int" +template t(a: string): string = "string" test "templates can be overloaded": check t(10) == "int" check t("test") == "string" test "previous definitions can be further overloaded or hidden in local scopes": - template t(a: bool): expr = "bool" + template t(a: bool): string = "bool" check t(true) == "bool" check t(10) == "int" - template t(a: int): expr = "inner int" + template t(a: int): string = "inner int" check t(10) == "inner int" check t("test") == "string" test "templates can be redefined multiple times": - template customAssert(cond: bool, msg: string): stmt {.immediate, dirty.} = + template customAssert(cond: bool, msg: string): typed {.immediate, dirty.} = if not cond: fail(msg) - template assertion_failed(body: stmt) {.immediate, dirty.} = - template fail(msg: string): stmt = body + template assertion_failed(body: typed) {.immediate, dirty.} = + template fail(msg: string): typed = body assertion_failed: check msg == "first fail path" customAssert false, "first fail path" assertion_failed: check msg == "second fail path" customAssert false, "second fail path" - diff --git a/tests/testament/categories.nim b/tests/testament/categories.nim index 4ba07cd21..7b1dd0df0 100644 --- a/tests/testament/categories.nim +++ b/tests/testament/categories.nim @@ -139,7 +139,7 @@ proc gcTests(r: var TResults, cat: Category, options: string) = " -d:release --gc:markAndSweep", cat, actionRun) template test(filename: untyped) = testWithoutBoehm filename - when not defined(windows): + when not defined(windows) and not defined(android): # AR: cannot find any boehm.dll on the net, right now, so disabled # for windows: testSpec r, makeTest("tests/gc" / filename, options & diff --git a/tests/testament/tester.nim b/tests/testament/tester.nim index b83eb668a..e4bbc3a00 100644 --- a/tests/testament/tester.nim +++ b/tests/testament/tester.nim @@ -64,12 +64,22 @@ let var targets = {low(TTarget)..high(TTarget)} -proc normalizeMsg(s: string): string = s.strip.replace("\C\L", "\L") +proc normalizeMsg(s: string): string = + result = newStringOfCap(s.len+1) + for x in splitLines(s): + if result.len > 0: result.add '\L' + result.add x.strip + +proc getFileDir(filename: string): string = + result = filename.splitFile().dir + if not result.isAbsolute(): + result = getCurrentDir() / result proc callCompiler(cmdTemplate, filename, options: string, target: TTarget): TSpec = let c = parseCmdLine(cmdTemplate % ["target", targetToCmd[target], - "options", options, "file", filename.quoteShell]) + "options", options, "file", filename.quoteShell, + "filedir", filename.getFileDir()]) var p = startProcess(command=c[0], args=c[1.. ^1], options={poStdErrToStdOut, poUsePath}) let outp = p.outputStream @@ -114,7 +124,8 @@ proc callCompiler(cmdTemplate, filename, options: string, proc callCCompiler(cmdTemplate, filename, options: string, target: TTarget): TSpec = let c = parseCmdLine(cmdTemplate % ["target", targetToCmd[target], - "options", options, "file", filename.quoteShell]) + "options", options, "file", filename.quoteShell, + "filedir", filename.getFileDir()]) var p = startProcess(command="gcc", args=c[5.. ^1], options={poStdErrToStdOut, poUsePath}) let outp = p.outputStream diff --git a/tests/trmacros/targlist.nim b/tests/trmacros/targlist.nim index f9d2cb6c6..46235dab1 100644 --- a/tests/trmacros/targlist.nim +++ b/tests/trmacros/targlist.nim @@ -3,7 +3,7 @@ discard """ """ proc f(x: varargs[string, `$`]) = discard -template optF{f(x)}(x: varargs[expr]) = +template optF{f(x)}(x: varargs[untyped]) = writeLine(stdout, x) f 1, 2, false, 3, "ha" diff --git a/tests/trmacros/tcse.nim b/tests/trmacros/tcse.nim index 023a8f298..315570d8f 100644 --- a/tests/trmacros/tcse.nim +++ b/tests/trmacros/tcse.nim @@ -2,8 +2,8 @@ discard """ output: "4" """ -template cse{f(a, a, x)}(a: expr{(nkDotExpr|call|nkBracketExpr)&noSideEffect}, - f: expr, x: varargs[expr]): expr = +template cse{f(a, a, x)}(a: typed{(nkDotExpr|call|nkBracketExpr)&noSideEffect}, + f: typed, x: varargs[typed]): untyped = let aa = a f(aa, aa, x)+4 diff --git a/tests/trmacros/tmatrix.nim b/tests/trmacros/tmatrix.nim index f409434c5..a14ad2db0 100644 --- a/tests/trmacros/tmatrix.nim +++ b/tests/trmacros/tmatrix.nim @@ -15,7 +15,7 @@ proc `$`(a: TMat): string = result = $a.dummy proc mat21(): TMat = result.dummy = 21 -macro optOps{ (`+`|`-`|`*`) ** a }(a: TMat): expr = +macro optOps{ (`+`|`-`|`*`) ** a }(a: TMat): untyped = echo treeRepr(a) result = newCall(bindSym"mat21") diff --git a/tests/trmacros/tnoalias.nim b/tests/trmacros/tnoalias.nim index 1d5671362..ec12d4712 100644 --- a/tests/trmacros/tnoalias.nim +++ b/tests/trmacros/tnoalias.nim @@ -2,7 +2,7 @@ discard """ output: "23" """ -template optslice{a = b + c}(a: expr{noalias}, b, c: expr): stmt = +template optslice{a = b + c}(a: untyped{noalias}, b, c: untyped): typed = a = b inc a, c diff --git a/tests/trmacros/tnoalias2.nim b/tests/trmacros/tnoalias2.nim index 5a816acb9..9362e764f 100644 --- a/tests/trmacros/tnoalias2.nim +++ b/tests/trmacros/tnoalias2.nim @@ -3,7 +3,7 @@ discard """ """ # bug #206 -template optimizeOut{testFunc(a, b)}(a: int, b: int{alias}) : expr = 0 +template optimizeOut{testFunc(a, b)}(a: int, b: int{alias}): untyped = 0 proc testFunc(a, b: int): int = result = a + b var testVar = 1 diff --git a/tests/trmacros/tnoendlessrec.nim b/tests/trmacros/tnoendlessrec.nim index 53891bcc0..508770ca7 100644 --- a/tests/trmacros/tnoendlessrec.nim +++ b/tests/trmacros/tnoendlessrec.nim @@ -4,7 +4,7 @@ discard """ # test that an endless recursion is avoided: -template optLen{len(x)}(x: expr): expr = len(x) +template optLen{len(x)}(x: typed): int = len(x) var s = "lala" echo len(s) diff --git a/tests/trmacros/tor.nim b/tests/trmacros/tor.nim index 500851582..d698e928d 100644 --- a/tests/trmacros/tor.nim +++ b/tests/trmacros/tor.nim @@ -4,13 +4,13 @@ true 3''' """ -template arithOps: expr = (`+` | `-` | `*`) -template testOr{ (arithOps{f})(a, b) }(a, b, f: expr): expr = f(a+1, b) +template arithOps: untyped = (`+` | `-` | `*`) +template testOr{ (arithOps{f})(a, b) }(a, b, f: untyped): untyped = f(a+1, b) let xx = 10 echo 10*xx -template t{x = (~x){y} and (~x){z}}(x, y, z: bool): stmt = +template t{x = (~x){y} and (~x){z}}(x, y, z: bool): typed = x = y if x: x = z @@ -22,7 +22,7 @@ a = b and a echo a # bug #798 -template t012{(0|1|2){x}}(x: expr): expr = x+1 +template t012{(0|1|2){x}}(x: untyped): untyped = x+1 let z = 1 # outputs 3 thanks to fixpoint iteration: echo z diff --git a/tests/trmacros/tpartial.nim b/tests/trmacros/tpartial.nim index fdaa3414a..c636684d7 100644 --- a/tests/trmacros/tpartial.nim +++ b/tests/trmacros/tpartial.nim @@ -5,7 +5,7 @@ discard """ proc p(x, y: int; cond: bool): int = result = if cond: x + y else: x - y -template optP{p(x, y, true)}(x, y: expr): expr = x - y -template optP{p(x, y, false)}(x, y: expr): expr = x + y +template optP{p(x, y, true)}(x, y): untyped = x - y +template optP{p(x, y, false)}(x, y): untyped = x + y echo p(2, 4, true) diff --git a/tests/trmacros/tstar.nim b/tests/trmacros/tstar.nim index 536289ff0..86f698232 100644 --- a/tests/trmacros/tstar.nim +++ b/tests/trmacros/tstar.nim @@ -10,7 +10,7 @@ proc `&&`(s: varargs[string]): string = for i in 1..len(s)-1: result.add s[i] inc calls -template optConc{ `&&` * a }(a: string): expr = &&a +template optConc{ `&&` * a }(a: string): string = &&a let space = " " echo "my" && (space & "awe" && "some " ) && "concat" diff --git a/tests/trmacros/tstmtlist.nim b/tests/trmacros/tstmtlist.nim index 5202f778b..751acb79a 100644 --- a/tests/trmacros/tstmtlist.nim +++ b/tests/trmacros/tstmtlist.nim @@ -8,7 +8,7 @@ discard """ template optWrite{ write(f, x) ((write|writeLine){w})(f, y) -}(x, y: varargs[expr], f, w: expr) = +}(x, y: varargs[untyped], f, w: untyped) = w(f, "|", x, y, "|") if true: diff --git a/tests/tuples/tuple_with_nil.nim b/tests/tuples/tuple_with_nil.nim index 9b5d583d3..7f5a359f5 100644 --- a/tests/tuples/tuple_with_nil.nim +++ b/tests/tuples/tuple_with_nil.nim @@ -758,7 +758,7 @@ proc addfmtfmt(fmtstr: string; args: NimNode; retvar: NimNode): NimNode {.compil if arg.cnt == 0: warning("Argument " & $(i+1) & " `" & args[i].repr & "` is not used in format string") -macro addfmt(s: var string, fmtstr: string{lit}, args: varargs[expr]): expr = +macro addfmt(s: var string, fmtstr: string{lit}, args: varargs[typed]): untyped = ## The same as `s.add(fmtstr.fmt(args...))` but faster. result = addfmtfmt($fmtstr, args, s) diff --git a/tests/typerel/temptynode.nim b/tests/typerel/temptynode.nim index b32b16121..32148ce13 100644 --- a/tests/typerel/temptynode.nim +++ b/tests/typerel/temptynode.nim @@ -10,7 +10,7 @@ import macros proc blah(x: proc (a, b: int): int) = echo x(5, 5) -macro test(): stmt = +macro test(): untyped = result = newNimNode(nnkEmpty) blah(test()) diff --git a/tests/typerel/tsymchoice_for_expr.nim b/tests/typerel/tsymchoice_for_expr.nim index 4c1f52bef..394b22704 100644 --- a/tests/typerel/tsymchoice_for_expr.nim +++ b/tests/typerel/tsymchoice_for_expr.nim @@ -1,6 +1,6 @@ # bug #1988 -template t(e: expr) = discard +template t(e) = discard proc positive(x: int): int = +x proc negative(x: int): int = -x diff --git a/tests/typerel/tvarargsexpr.nim b/tests/typerel/tvarargsexpr.nim index c6a59fb20..092d50076 100644 --- a/tests/typerel/tvarargsexpr.nim +++ b/tests/typerel/tvarargsexpr.nim @@ -8,7 +8,7 @@ true''' import macros -macro thirteen(args: varargs[expr]): expr = +macro thirteen(args: varargs[untyped]): int = result = newIntLitNode(13) doAssert(13==thirteen([1,2])) # works @@ -22,7 +22,8 @@ echo "success" # bug #2545 import macros -macro test(e: varargs[untyped]): expr = bindSym"true" +macro test(e: varargs[untyped]): untyped = + bindSym"true" echo test(a) echo test(fake=90, arguments=80, also="false", possible=true) diff --git a/tests/types/taliasassignment.nim b/tests/types/taliasassignment.nim deleted file mode 100644 index bf4fe8520..000000000 --- a/tests/types/taliasassignment.nim +++ /dev/null @@ -1,50 +0,0 @@ -discard """ - output: '''19 -(c: 0) -(c: 13) -@[(c: 11)] -@[(c: 17)]''' -""" -# bug #5238 - -type - Rgba8 = object - c: int - BlenderRgb*[ColorT] = object - -template getColorType*[C](x: typedesc[BlenderRgb[C]]): typedesc = C - -type - ColorT = getColorType(BlenderRgb[int]) - -proc setColor(c: var ColorT) = - c = 19 - -var n: ColorT -n.setColor() -echo n - -type - ColorType = getColorType(BlenderRgb[Rgba8]) - -var x: ColorType -echo x - -proc setColor(c: var ColorType) = - c = Rgba8(c: 13) - -proc setColor(c: var seq[ColorType]) = - c[0] = Rgba8(c: 11) - -proc setColorArray(c: var openArray[ColorType]) = - c[0] = Rgba8(c: 17) - -x.setColor() -echo x - -var y = @[Rgba8(c:15)] -y.setColor() -echo y - -y.setColorArray() -echo y \ No newline at end of file diff --git a/tests/types/taliasbugs.nim b/tests/types/taliasbugs.nim new file mode 100644 index 000000000..57254760a --- /dev/null +++ b/tests/types/taliasbugs.nim @@ -0,0 +1,158 @@ +discard """ + msg: '''true +true +true +true +true +true''' + output: '''true +true +true +true +true +true +R +R +R +R +19 +(c: 0) +(c: 13) +@[(c: 11)] +@[(c: 17)] +100''' +""" + +# bug #5360 +import macros + +type + Order = enum + R + OrderAlias = Order + +template getOrderTypeA(): typedesc = Order +template getOrderTypeB(): typedesc = OrderAlias + +type + OrderR = getOrderTypeA() + OrderG = getOrderTypeB() + +macro typeRep(a, b: typed): untyped = + if sameType(a, b): + echo "true" + else: + echo "false" + +template test(a, b: typedesc) = + when a is b: + echo "true" + else: + echo "false" + +test(OrderAlias, Order) +test(OrderR, Order) +test(OrderG, Order) + +test(OrderR, OrderG) +test(OrderR, OrderAlias) +test(OrderG, OrderAlias) + +typeRep(OrderAlias.R, Order.R) # true +typeRep(OrderR.R, Order.R) # true +typeRep(OrderG.R, Order.R) # true + +typeRep(OrderR.R, OrderAlias.R) # true +typeRep(OrderG.R, OrderAlias.R) # true +typeRep(OrderR.R, OrderG.R) # true + +echo OrderR.R # R +echo OrderG.R # R +echo OrderAlias.R # R +echo Order.R # R + +# bug #5238 + +type + Rgba8 = object + c: int + BlenderRgb*[ColorT] = object + +template getColorType*[C](x: typedesc[BlenderRgb[C]]): typedesc = C + +type + ColorT = getColorType(BlenderRgb[int]) + +proc setColor(c: var ColorT) = + c = 19 + +var n: ColorT +n.setColor() +echo n + +type + ColorType = getColorType(BlenderRgb[Rgba8]) + +var x: ColorType +echo x + +proc setColor(c: var ColorType) = + c = Rgba8(c: 13) + +proc setColor(c: var seq[ColorType]) = + c[0] = Rgba8(c: 11) + +proc setColorArray(c: var openArray[ColorType]) = + c[0] = Rgba8(c: 17) + +x.setColor() +echo x + +var y = @[Rgba8(c:15)] +y.setColor() +echo y + +y.setColorArray() +echo y + +#bug #6016 +type + Onion {.union.} = object + field1: int + field2: uint64 + + Stroom = Onion + + PStroom = ptr Stroom + +proc pstruct(u: PStroom) = + echo u.field2 + +var oni = Onion(field1: 100) +pstruct(oni.addr) + + +# bug #4124 + +import sequtils + +type + Foo = distinct string + +var + foo: Foo + +type + Alias = (type(foo)) +var + a: Alias + +a = foo + +when true: + var xs = @[1,2,3] + + proc asFoo(i: string): Foo = + Foo(i) + + var xx = xs.mapIt(asFoo($(it + 5))) diff --git a/tests/types/taliasinequality.nim b/tests/types/taliasinequality.nim deleted file mode 100644 index f3ecd536a..000000000 --- a/tests/types/taliasinequality.nim +++ /dev/null @@ -1,66 +0,0 @@ -discard """ - msg: '''true -true -true -true -true -true''' - output: '''true -true -true -true -true -true -R -R -R -R''' -""" - -# bug #5360 -import macros - -type - Order = enum - R - OrderAlias = Order - -template getOrderTypeA(): typedesc = Order -template getOrderTypeB(): typedesc = OrderAlias - -type - OrderR = getOrderTypeA() - OrderG = getOrderTypeB() - -macro typeRep(a, b: typed): untyped = - if sameType(a, b): - echo "true" - else: - echo "false" - -template test(a, b: typedesc) = - when a is b: - echo "true" - else: - echo "false" - -test(OrderAlias, Order) -test(OrderR, Order) -test(OrderG, Order) - -test(OrderR, OrderG) -test(OrderR, OrderAlias) -test(OrderG, OrderAlias) - -typeRep(OrderAlias.R, Order.R) # true -typeRep(OrderR.R, Order.R) # true -typeRep(OrderG.R, Order.R) # true - -typeRep(OrderR.R, OrderAlias.R) # true -typeRep(OrderG.R, OrderAlias.R) # true -typeRep(OrderR.R, OrderG.R) # true - -echo OrderR.R # R -echo OrderG.R # R -echo OrderAlias.R # R -echo Order.R # R \ No newline at end of file diff --git a/tests/types/tauto_canbe_void.nim b/tests/types/tauto_canbe_void.nim index 60e83c510..fd42cb701 100644 --- a/tests/types/tauto_canbe_void.nim +++ b/tests/types/tauto_canbe_void.nim @@ -1,7 +1,7 @@ import future -template tempo(s: expr) = +template tempo(s) = s("arg") tempo((s: string)->auto => echo(s)) diff --git a/tests/types/tisopr.nim b/tests/types/tisopr.nim index 14999ebee..2f9dbf245 100644 --- a/tests/types/tisopr.nim +++ b/tests/types/tisopr.nim @@ -17,10 +17,10 @@ proc IsVoid[T](): string = const x = int is int echo x, " ", float is float, " ", float is string, " ", IsVoid[void]() -template yes(e: expr): stmt = +template yes(e): void = static: assert e -template no(e: expr): stmt = +template no(e): void = static: assert(not e) when false: diff --git a/tests/types/typeof_produces_alias.nim b/tests/types/typeof_produces_alias.nim deleted file mode 100644 index 44cb00c94..000000000 --- a/tests/types/typeof_produces_alias.nim +++ /dev/null @@ -1,25 +0,0 @@ - -# bug #4124 - -import sequtils - -type - Foo = distinct string - -var - foo: Foo - -type - Alias = (type(foo)) -var - a: Alias - -a = foo - -when true: - var xs = @[1,2,3] - - proc asFoo(i: string): Foo = - Foo(i) - - var xx = xs.mapIt(asFoo($(it + 5))) diff --git a/tests/untestable/readme.markdown b/tests/untestable/readme.markdown new file mode 100644 index 000000000..fcb7f4f28 --- /dev/null +++ b/tests/untestable/readme.markdown @@ -0,0 +1,2 @@ +This directory contains tests which are not automatically executed +for various reasons. Mainly due to dependencies on external services. \ No newline at end of file diff --git a/tests/usingstmt/tusingstatement.nim b/tests/usingstmt/tusingstatement.nim index 0d76b2423..8585bcc9e 100644 --- a/tests/usingstmt/tusingstatement.nim +++ b/tests/usingstmt/tusingstatement.nim @@ -12,7 +12,7 @@ import # Nim's destructors offer a mechanism for automatic # disposal of resources. # -macro autoClose(e: expr): stmt {.immediate.} = +macro autoClose(args: varargs[untyped]): untyped = let e = callsite() if e.len != 3: error "Using statement: unexpected number of arguments. Got " & @@ -85,5 +85,3 @@ proc use(r: var TResource) = autoClose(r = openResource("test")): use r - - diff --git a/tests/vm/tanonproc.nim b/tests/vm/tanonproc.nim index 474b768ca..c5cb57d75 100644 --- a/tests/vm/tanonproc.nim +++ b/tests/vm/tanonproc.nim @@ -42,7 +42,7 @@ proc getOrElse[T](o: Option[T], def: T): T = proc quoteStr(s: string): Option[string] = s.some.notEmpty.map(v => "`" & v & "`") -macro str(s: string): stmt = +macro str(s: string): typed = let x = s.strVal let y = quoteStr(x) let sn = newStrLitNode(y.getOrElse("NONE")) diff --git a/tests/vm/tasmparser.nim b/tests/vm/tasmparser.nim index fbacdbc87..d70c629b6 100644 --- a/tests/vm/tasmparser.nim +++ b/tests/vm/tasmparser.nim @@ -10,10 +10,10 @@ var cpp {.compileTime.} = "" token {.compileTime.} = "" -proc log (msg: string) {.compileTime.} = +proc log(msg: string) {.compileTime.} = echo msg -proc asmx64 () {.compileTime} = +proc asmx64() {.compileTime} = #log "code = $1" % code @@ -36,7 +36,7 @@ proc asmx64 () {.compileTime} = const end_or_symbol_or_comment_or_passthrough = symbolStart + end_or_comment + passthrough_start - proc abortAsmParse (err:string) = + proc abortAsmParse(err:string) = discard let codeLen = code.len @@ -49,17 +49,17 @@ proc asmx64 () {.compileTime} = var state:asmParseState = leading - proc checkEnd (err:string) = - let ch = code [start] - if int (ch) == 0: - abortAsmParse (err) + proc checkEnd(err:string) = + let ch = code[start] + if int(ch) == 0: + abortAsmParse(err) - proc get_passthrough () = + proc get_passthrough() = inc start let prev_start = start let prev_token = token - start += code.parseUntil (token, passthrough_end, start) - checkEnd ("Failed to find passthrough end delimiter from offset $1 for:$2\n$3" % [$prev_start, $(code [prev_start-prev_token.len..prev_start]), token[1..token.len-1]]) + start += code.parseUntil(token, passthrough_end, start) + checkEnd("Failed to find passthrough end delimiter from offset $1 for:$2\n$3" % [$prev_start, $(code[prev_start-prev_token.len..prev_start]), token[1..token.len-1]]) inc start cpp.add "`" cpp.add token @@ -67,27 +67,27 @@ proc asmx64 () {.compileTime} = var inparse = true - proc checkCmdEnd () = + proc checkCmdEnd() = if codeLen == start: state = endCmd inparse = false while inparse: - checkCmdEnd () + checkCmdEnd() - log ("state=$1 start=$2" % [$state, $start]) + log("state=$1 start=$2" % [$state, $start]) case state: of leading: echo "b100 ", start - start += code.skipWhile (leadingWhiteSpace, start) + start += code.skipWhile(leadingWhiteSpace, start) echo "b200 ", start - let ch = code [start] + let ch = code[start] if ch in endOfLine: - inc (line) + inc(line) #echo "c100 ", start, ' ', code - start += code.skipWhile (endOfline, start) + start += code.skipWhile(endOfline, start) #echo "c200 ", start, ' ', code continue elif ch in symbolStart: @@ -95,20 +95,20 @@ proc asmx64 () {.compileTime} = elif ch in eolComment: state = skipToEndOfLine elif ch in passthrough_start: - get_passthrough () + get_passthrough() echo "d100 ", start - start += code.parseUntil (token, end_or_symbol_or_comment_or_passthrough, start) + start += code.parseUntil(token, end_or_symbol_or_comment_or_passthrough, start) echo "d200 ", start cpp.add token state = mnemonic - elif int (ch) == 0: + elif int(ch) == 0: break else: - abortAsmParse ("after '$3' illegal character at offset $1: $2" % [$start, $(int (ch)), token]) + abortAsmParse("after '$3' illegal character at offset $1: $2" % [$start, $(int(ch)), token]) of mnemonic: echo "e100 ", start - start += code.parseWhile (token, symbol, start) + start += code.parseWhile(token, symbol, start) echo "e200 ", start cpp.add xp cpp.add token @@ -118,29 +118,29 @@ proc asmx64 () {.compileTime} = of betweenArguments: let tmp = start let rcode = code - start += rcode.parseUntil (token, end_or_symbol_or_comment_or_passthrough, tmp) + start += rcode.parseUntil(token, end_or_symbol_or_comment_or_passthrough, tmp) cpp.add token if codeLen <= start: state = endCmd continue - let ch = code [start] + let ch = code[start] if ch in passthrough_start: - get_passthrough () + get_passthrough() continue - if (ch in {'x', 'X'}) and ('0' == code [start-1]): - token = $(code [start]) + if(ch in {'x', 'X'}) and('0' == code[start-1]): + token = $(code[start]) cpp.add token inc start continue state = arguments of arguments: - if code [start] in end_or_comment: + if code[start] in end_or_comment: state = endCmd continue - start += code.parseWhile (token, symbol, start) + start += code.parseWhile(token, symbol, start) cpp.add xp cpp.add token state = betweenArguments @@ -151,21 +151,21 @@ proc asmx64 () {.compileTime} = of skipToEndOfLine: echo "a100 ", start - start += code.skipUntil (endOfLine, start) + start += code.skipUntil(endOfLine, start) echo "a200 ", start - start += code.skipWhile (endOfline, start) + start += code.skipWhile(endOfline, start) echo "a300 ", start inc line state = leading cpp.add asmx64post - echo ($cpp) + echo($cpp) -macro asmx64x (code_in:expr) : stmt = +macro asmx64x(code_in:untyped) : typed = code = $code_in - echo ("code.len = $1, code = >>>$2<<<" % [$code.len, code]) - asmx64 () + echo("code.len = $1, code = >>>$2<<<" % [$code.len, code]) + asmx64() discard result asmx64x """ diff --git a/tests/vm/tcomponent.nim b/tests/vm/tcomponent.nim index efeba2a6d..e7962e7ab 100644 --- a/tests/vm/tcomponent.nim +++ b/tests/vm/tcomponent.nim @@ -70,12 +70,12 @@ proc parse_component(body: NimNode): Component = result.procs_index.add(procdef.identifier.name) else: discard -macro component*(name: expr, body: stmt): stmt {.immediate.} = +macro component*(name, body: untyped): typed = let component = parse_component(body) registry.addComponent($name, component) parseStmt("discard") -macro component_builtins(body: stmt): stmt {.immediate.} = +macro component_builtins(body: untyped): typed = let builtin = parse_component(body) registry.field_index = builtin.field_index registry.procs_index = builtin.procs_index @@ -88,7 +88,7 @@ proc bind_methods*(component: var Component, identifier: Ident): seq[NimNode] = procdef.params.insert(this_field, 0) result.add(procdef.render()) -macro bind_components*(type_name, component_names: expr): stmt {.immediate.} = +macro bind_components*(type_name, component_names: untyped): typed = result = newStmtList() let identifier = newIdent(type_name) let components = newBracket(component_names) diff --git a/tests/vm/teval1.nim b/tests/vm/teval1.nim index 1d3a68a0d..0eaa050da 100644 --- a/tests/vm/teval1.nim +++ b/tests/vm/teval1.nim @@ -5,7 +5,7 @@ proc testProc: string {.compileTime.} = result = result & "" when true: - macro test(n: stmt): stmt {.immediate.} = + macro test(n: untyped): untyped = result = newNimNode(nnkStmtList) echo "#", testProc(), "#" test: @@ -20,5 +20,3 @@ echo "##", x, "##" static: var i, j: set[int8] = {} var k = i + j - - diff --git a/tests/vm/texcl.nim b/tests/vm/texcl.nim index 4ccfd6bfa..e23a423fe 100644 --- a/tests/vm/texcl.nim +++ b/tests/vm/texcl.nim @@ -15,13 +15,13 @@ proc initOpts(): set[nlOptions] = result.incl nloDebug result.incl nloNone result.excl nloDebug - + const cOpts = initOpts() -macro nlo(): stmt = +macro nlo(): typed = nlOpts.incl(nloNone) nlOpts.excl(nloDebug) result = newEmptyNode() nlo() -echo nloDebug in cOpts \ No newline at end of file +echo nloDebug in cOpts diff --git a/tests/vm/tmitems.nim b/tests/vm/tmitems.nim index 4ee225eed..a0e64d6aa 100644 --- a/tests/vm/tmitems.nim +++ b/tests/vm/tmitems.nim @@ -7,7 +7,7 @@ discard """ # bug #3731 var list {.compileTime.} = newSeq[int]() -macro calc*(): stmt {.immediate.} = +macro calc*(): typed = list.add(1) for c in list.mitems: c = 13 @@ -19,7 +19,7 @@ calc() # bug #3859 import macros -macro m: stmt = +macro m: typed = var s = newseq[NimNode](3) # var s: array[3,NimNode] # not working either for i in 0..<s.len: s[i] = newLit(3) # works @@ -29,3 +29,17 @@ macro m: stmt = result.add newCall(bindsym"echo", i) m() + +# bug 4741 & 5013 +proc test() = + var s = [("baz", 42), ("bath", 42)] + for i in s.mitems: + i[1] = 3 + doAssert(s == [("baz", 3), ("bath", 3)]) + +static: + test() + var s = [("baz", 42), ("bath", 42)] + for i in s.mitems: + i[1] = 3 + doAssert(s == [("baz", 3), ("bath", 3)]) diff --git a/tests/vm/tseq_badinit.nim b/tests/vm/tseq_badinit.nim new file mode 100644 index 000000000..15889d60e --- /dev/null +++ b/tests/vm/tseq_badinit.nim @@ -0,0 +1,58 @@ + +type + AObj = object + i: int + d: float + ATup = tuple + i: int + d: float + MyEnum = enum + E01, E02, E03 + Myrange = range[0..10] + + MyProc = proc (x: int): bool + MyInt = distinct int + MyAlias = MyInt + MySet = set[char] + MyArray = array[4, char] + MySeq = seq[string] + +template test(typename, default: untyped) = + proc `abc typename`(): seq[typename] = + result = newSeq[typename]() + result.add(default) + result.setLen(3) + for i in 0 .. <2: + result[i] = default + + const constval = `abc typename`() + doAssert(constval == `abc typename`()) + + proc `arr typename`(): array[4, typename] = + for i in 0 .. <2: + result[i] = default + const constarr = `arr typename`() + doAssert(constarr == `arr typename`()) + +proc even(x: int): bool = x mod 2 == 0 +proc `==`(x, y: MyInt): bool = ord(x) == ord(y) +proc `$`(x: MyInt): string = $ord(x) +proc `$`(x: proc): string = + if x.isNil: "(nil)" else: "funcptr" + +test(int, 0) +test(uint, 0) +test(float, 0.1) +test(char, '0') +test(bool, false) +test(uint8, 2) +test(string, "data") +test(MyProc, even) +test(MyEnum, E02) +test(AObj, AObj()) +test(ATup, (i:11, d:9.99)) +test(Myrange, 4) +test(MyInt, MyInt(4)) +test(MyAlias, MyAlias(4)) +test(MyArray, ['0','1','2','3']) +test(MySeq, @["data"]) diff --git a/tests/vm/tstaticprintseq.nim b/tests/vm/tstaticprintseq.nim index e4a6aa081..246a0211b 100644 --- a/tests/vm/tstaticprintseq.nim +++ b/tests/vm/tstaticprintseq.nim @@ -24,7 +24,7 @@ bb const s = @[1,2,3] -macro foo: stmt = +macro foo: typed = for e in s: echo e @@ -34,7 +34,7 @@ static: for e in s: echo e -macro bar(x: static[seq[int]]): stmt = +macro bar(x: static[seq[int]]): untyped = for e in x: echo e @@ -55,7 +55,7 @@ static: var m2: TData = data for x in m2.numbers: echo x -macro ff(d: static[TData]): stmt = +macro ff(d: static[TData]): typed = for x in d.letters: echo x diff --git a/tests/vm/tstringnil.nim b/tests/vm/tstringnil.nim index bb546b698..39e4040dc 100644 --- a/tests/vm/tstringnil.nim +++ b/tests/vm/tstringnil.nim @@ -41,7 +41,7 @@ proc buildSuiteContents(suiteName, suiteDesc, suiteBloc: NimNode): tuple[tests: return (tests: tests) -macro suite(suiteName, suiteDesc: expr, suiteBloc: stmt): stmt {.immediate.} = +macro suite(suiteName, suiteDesc, suiteBloc: untyped): typed = let contents = buildSuiteContents(suiteName, suiteDesc, suiteBloc) # Test above diff --git a/tests/vm/ttouintconv.nim b/tests/vm/ttouintconv.nim index cd25ffb00..5f8884e80 100644 --- a/tests/vm/ttouintconv.nim +++ b/tests/vm/ttouintconv.nim @@ -20,7 +20,7 @@ msg: ''' #bug #2514 -macro foo(): stmt = +macro foo(): typed = var x = 8'u8 var y = 9'u16 var z = 17'u32 @@ -57,21 +57,21 @@ macro foo(): stmt = var zz = 0x7FFFFFFF'u32 echo zz - -macro foo2(): stmt = + +macro foo2(): typed = var xx = 0x7FFFFFFFFFFFFFFF echo xx - + var yy = 0 echo yy - + var zz = 0x80'u8 echo zz - + var ww = -9 var vv = ww.uint var kk = vv.uint32 echo kk - + foo() foo2() diff --git a/tests/vm/twrongconst.nim b/tests/vm/twrongconst.nim index 424ed080e..a329cb578 100644 --- a/tests/vm/twrongconst.nim +++ b/tests/vm/twrongconst.nim @@ -4,6 +4,6 @@ discard """ """ var x: array[100, char] -template foo : expr = x[42] +template foo : char = x[42] const myConst = foo diff --git a/tests/vm/tzero_extend.nim b/tests/vm/tzero_extend.nim new file mode 100644 index 000000000..a79105531 --- /dev/null +++ b/tests/vm/tzero_extend.nim @@ -0,0 +1,44 @@ + +const RANGE = -384.. -127 + +proc get_values(): (seq[int8], seq[int16], seq[int32]) = + let i8 = -3'i8 + let i16 = -3'i16 + let i32 = -3'i32 + doAssert i8.ze == 0xFD + doAssert i8.ze64 == 0xFD + doAssert i16.ze == 0xFFFD + doAssert i16.ze64 == 0xFFFD + + result[0] = @[]; result[1] = @[]; result[2] = @[] + + for offset in RANGE: + let i8 = -(1 shl 9) + offset + let i16 = -(1 shl 17) + offset + let i32 = -(1 shl 33) + offset + + # higher bits are masked. these should be exactly equal to offset. + result[0].add i8.toU8 + result[1].add i16.toU16 + result[2].add i32.toU32 + + +# these values this computed by VM +const COMPILETIME_VALUES = get_values() + +# these values this computed by compiler +let RUNTIME_VALUES = get_values() + +template check_values(int_type: static[int]) = + var index = 0 + let cvalues = COMPILETIME_VALUES[int_type] + let rvalues = RUNTIME_VALUES[int_type] + for offset in RANGE: + let moffset = cast[type(rvalues[0])](offset) + doAssert(moffset == rvalues[index] and moffset == cvalues[index], + "expected: " & $moffset & " got runtime: " & $rvalues[index] & " && compiletime: " & $cvalues[index] ) + inc(index) + +check_values(0) # uint8 +check_values(1) # uint16 +check_values(2) # uint32 diff --git a/tools/nim.bash-completion b/tools/nim.bash-completion new file mode 100644 index 000000000..4f62da986 --- /dev/null +++ b/tools/nim.bash-completion @@ -0,0 +1,47 @@ +# bash completion for nim -*- shell-script -*- + +_nim() +{ + local cur prev words cword split + _init_completion -s || return + + COMPREPLY=() + cur=${COMP_WORDS[COMP_CWORD]} + + if [ $COMP_CWORD -eq 1 ] ; then + # first item - suggest commands + kw="compile c doc doc2 compileToC cc compileToCpp cpp compileToOC objc js e rst2html rst2tex jsondoc jsondoc2 buildIndex genDepend dump check" + COMPREPLY=( $( compgen -W "${kw}" -- $cur ) ) + return 0 + fi + case $prev in + --stackTrace|--lineTrace|--threads|-x|--checks|--objChecks|--fieldChecks|--rangeChecks|--boundChecks|--overflowChecks|-a|--assertions|--floatChecks|--nanChecks|--infChecks|--deadCodeElim) + # Options that require on/off + [[ "$cur" == "=" ]] && cur="" + COMPREPLY=( $(compgen -W 'on off' -- "$cur") ) + return 0 + ;; + --opt) + [[ "$cur" == "=" ]] && cur="" + COMPREPLY=( $(compgen -W 'none speed size' -- "$cur") ) + return 0 + ;; + --app) + [[ "$cur" == "=" ]] && cur="" + COMPREPLY=( $(compgen -W 'console gui lib staticlib' -- "$cur") ) + return 0 + ;; + *) + kw="-r -p= --path= -d= --define= -u= --undef= -f --forceBuild --opt= --app= --stackTrace= --lineTrace= --threads= -x= --checks= --objChecks= --fieldChecks= --rangeChecks= --boundChecks= --overflowChecks= -a= --assertions= --floatChecks= --nanChecks= --infChecks= --deadCodeElim=" + COMPREPLY=( $( compgen -W "${kw}" -- $cur ) ) + _filedir '@(nim)' + #$split + return 0 + ;; + esac + return 0 + +} && +complete -onospace -F _nim nim + +# ex: ts=2 sw=2 et filetypesh diff --git a/tools/nim.zsh-completion b/tools/nim.zsh-completion new file mode 100644 index 000000000..3d6e0d5f6 --- /dev/null +++ b/tools/nim.zsh-completion @@ -0,0 +1,76 @@ +#compdef nim + +_nim() { + _arguments -C \ + ':command:(( + {compile,c}\:compile\ project\ with\ default\ code\ generator\ C + doc\:generate\ the\ documentation\ for\ inputfile + doc2\:generate\ the\ documentation\ for\ inputfile + {compileToC,cc}\:compile\ project\ with\ C\ code\ generator + {compileToCpp,cpp}\:compile\ project\ to\ C++\ code + {compileToOC,objc}\:compile\ project\ to\ Objective\ C\ code + js\:compile\ project\ to\ Javascript + e\:run\ a\ Nimscript\ file + rst2html\:convert\ a\ reStructuredText\ file\ to\ HTML + rst2tex\:convert\ a\ reStructuredText\ file\ to\ TeX + jsondoc\:extract\ the\ documentation\ to\ a\ json\ file + jsondoc2\:extract\ documentation\ to\ a\ json\ file\ using\ doc2 + buildIndex\:build\ an\ index\ for\ the\ whole\ documentation + genDepend\:generate\ a\ DOT\ file\ containing\ the\ module\ dependency\ graph + dump\:dump\ all\ defined\ conditionals\ and\ search\ paths + check\:checks\ the\ project\ for\ syntax\ and\ semantic + ))' \ + '*-r[run the application]' \ + '*--run[run the application]' \ + '*-p=[add path to search paths]' \ + '*--path=[add path to search paths]' \ + '*-d=[define a conditional symbol]' \ + '*--define=[define a conditional symbol]' \ + '*-u=[undefine a conditional symbol]' \ + '*--undef=[undefine a conditional symbol]' \ + '*-f[force rebuilding of all modules]' \ + '*--forceBuild[force rebuilding of all modules]' \ + '*--stackTrace=on[turn stack tracing on]' \ + '*--stackTrace=off[turn stack tracing off]' \ + '*--lineTrace=on[turn line tracing on]' \ + '*--lineTrace=off[turn line tracing off]' \ + '*--threads=on[turn support for multi-threading on]' \ + '*--threads=off[turn support for multi-threading off]' \ + '*-x=on[turn all runtime checks on]' \ + '*-x=off[turn all runtime checks off]' \ + '*--checks=on[turn all runtime checks on]' \ + '*--checks=off[turn all runtime checks off]' \ + '*--objChecks=on[turn obj conversion checks on]' \ + '*--objChecks=off[turn obj conversion checks off]' \ + '*--fieldChecks=on[turn case variant field checks on]' \ + '*--fieldChecks=off[turn case variant field checks off]' \ + '*--rangeChecks=on[turn range checks on]' \ + '*--rangeChecks=off[turn range checks off]' \ + '*--boundChecks=on[turn bound checks on]' \ + '*--boundChecks=off[turn bound checks off]' \ + '*--overflowChecks=on[turn int over-/underflow checks on]' \ + '*--overflowChecks=off[turn int over-/underflow checks off]' \ + '*-a[turn assertions on]' \ + '*-a[turn assertions off]' \ + '*--assertions=on[turn assertions on]' \ + '*--assertions=off[turn assertions off]' \ + '*--floatChecks=on[turn all floating point (NaN/Inf) checks on]' \ + '*--floatChecks=off[turn all floating point (NaN/Inf) checks off]' \ + '*--nanChecks=on[turn NaN checks on]' \ + '*--nanChecks=off[turn NaN checks off]' \ + '*--infChecks=on[turn Inf checks on]' \ + '*--infChecks=off[turn Inf checks off]' \ + '*--deadCodeElim=on[whole program dead code elimination on]' \ + '*--deadCodeElim=off[whole program dead code elimination off]' \ + '*--opt=none[do not optimize]' \ + '*--opt=speed[optimize for speed|size - use -d:release for a release build]' \ + '*--opt=size[optimize for size]' \ + '*--debugger:native|endb[use native debugger (gdb) | ENDB (experimental)]' \ + '*--app=console[generate a console app]' \ + '*--app=gui[generate a GUI app]' \ + '*--app=lib[generate a dynamic library]' \ + '*--app=staticlib[generate a static library]' \ + ':filename:_files -g"*.nim"' +} + +_nim "$@" diff --git a/tools/niminst/buildsh.tmpl b/tools/niminst/buildsh.tmpl index e90ad97c0..faa8a47d0 100644 --- a/tools/niminst/buildsh.tmpl +++ b/tools/niminst/buildsh.tmpl @@ -1,4 +1,4 @@ -#? stdtmpl(subsChar='?') | standard +#? stdtmpl(subsChar='?') | standard #proc generateBuildShellScript(c: ConfigData): string = # result = "#! /bin/sh\n# Generated from niminst\n" & # "# Template is in tools/niminst/buildsh.tmpl\n" & @@ -9,6 +9,18 @@ set -e while : do case "$1" in + --os) + optos=$2 + shift 2 + ;; + --cpu) + optcpu=$2 + shift 2 + ;; + --osname) + optosname=$2 + shift 2 + ;; --extraBuildArgs) extraBuildArgs=" $2" shift 2 @@ -35,6 +47,7 @@ PS4="" # add(result, "# platform detection\n") ucpu=`uname -m` uos=`uname` +uosname=`uname -o` # add(result, "# bin dir detection\n") binDir=?{firstBinPath(c).toUnix} @@ -46,9 +59,21 @@ if [ ! -d $binDir ]; then mkdir $binDir fi +# add(result, "# override OS, CPU and OS Name with command-line arguments\n") +if [ -n "$optos" ]; then + uos="$optos" +fi +if [ -n "$optcpu" ]; then + ucpu="$optcpu" +fi +if [ -n "$optcpu" ]; then + uosname="$optosname" +fi + # add(result, "# convert to lower case:\n") ucpu=`echo $ucpu | tr "[:upper:]" "[:lower:]"` uos=`echo $uos | tr "[:upper:]" "[:lower:]"` +uosname=`echo $uosname | tr "[:upper:]" "[:lower:]"` case $uos in *linux* ) @@ -97,6 +122,11 @@ case $uos in *mingw* ) myos="windows" ;; + *android* ) + myos="android" + LINK_FLAGS="$LINK_FLAGS -ldl -lm -lrt" + LINK_FLAGS="$LINK_FLAGS -landroid-glob" + ;; *) echo 2>&1 "Error: unknown operating system: $uos" exit 1 @@ -123,8 +153,16 @@ case $ucpu in *power*|*ppc* ) mycpu="powerpc" ;; *mips* ) - mycpu="mips" ;; - *arm*|*armv6l* ) + mycpu="$("$CC" -dumpmachine | sed 's/-.*//')" + case $mycpu in + mips|mipsel|mips64|mips64el) + ;; + *) + echo 2>&1 "Error: unknown MIPS target: $mycpu" + exit 1 + esac + ;; + *arm*|*armv6l*|*armv71* ) mycpu="arm" ;; *aarch64* ) mycpu="arm64" ;; @@ -134,7 +172,17 @@ case $ucpu in ;; esac +case $uosname in + *android* ) + LINK_FLAGS="$LINK_FLAGS -landroid-glob" + myosname="android" + myos="android" + ;; +esac + # add(result, "# call the compiler:\n") +echo \# OS: $myos +echo \# CPU: $mycpu case $myos in # for osA in 1..c.oses.len: diff --git a/tools/niminst/makefile.tmpl b/tools/niminst/makefile.tmpl index c4e0be55e..4a20680e0 100644 --- a/tools/niminst/makefile.tmpl +++ b/tools/niminst/makefile.tmpl @@ -17,6 +17,7 @@ endif ucpu := $(shell sh -c 'uname -m | tr "[:upper:]" "[:lower:]"') uos := $(shell sh -c 'uname | tr "[:upper:]" "[:lower:]"') +uosname := $(shell sh -c 'uname -o | tr "[:upper:]" "[:lower:]"') ifeq ($(uos),linux) myos = linux @@ -68,6 +69,11 @@ ifndef uos $(error unknown operating system: $(uos)) endif +ifeq ($(uosname),android) + myos = android + LINK_FLAGS += -landroid-glob +endif + ifeq ($(ucpu),i386) mycpu = i386 endif @@ -114,8 +120,11 @@ endif ifeq ($(ucpu),ppc) mycpu = ppc endif -ifeq ($(ucpu),mips) - mycpu = mips +ifneq (,$(filter $(ucpu), mips mips64)) + mycpu = $(shell /bin/sh -c '"$(CC)" -dumpmachine | sed "s/-.*//"') + ifeq (,$(filter $(mycpu), mips mipsel mips64 mips64el)) + $(error unknown MIPS target: $(mycpu)) + endif endif ifeq ($(ucpu),arm) mycpu = arm diff --git a/tools/start.bat b/tools/start.bat index a4475fac7..8e345998c 100644 --- a/tools/start.bat +++ b/tools/start.bat @@ -1,5 +1,6 @@ @echo off REM COLOR 0A +chcp 65001 SET NIMPATH=%~dp0\.. SET PATH=%NIMPATH%\bin;%NIMPATH%\dist\mingw\bin;%PATH% cd %NIMPATH% diff --git a/tools/winrelease.nim b/tools/winrelease.nim new file mode 100644 index 000000000..5a687cfaa --- /dev/null +++ b/tools/winrelease.nim @@ -0,0 +1,9 @@ +## This is a small helper program to build the Win release. +## This used to be part of koch (and it still uses koch as a library) +## but since 'koch.exe' cannot overwrite itself is now its own program. +## The problem is that 'koch.exe' too is part of the zip bundle and +## needs to have the right 32/64 bit version. (Bug #6147) + +import "../koch" + +winRelease() diff --git a/web/nimblepkglist.nim b/web/nimblepkglist.nim index 7070f281b..870944ab0 100644 --- a/web/nimblepkglist.nim +++ b/web/nimblepkglist.nim @@ -33,6 +33,7 @@ proc processContent(content: string) = for pkg in jsonArr: assert pkg.kind == JObject + if not pkg.hasKey"url": continue let pkgWeb = if pkg.hasKey("web"): pkg["web"].str else: pkg["url"].str @@ -40,7 +41,7 @@ proc processContent(content: string) = desc = pkg["description"].str dot = if desc.high > 0 and desc[desc.high] in endings: "" else: "." listItem = li(a(href=pkgWeb, pkg["name"].str), " ", desc & dot) - if pkg["url"].str.startsWith("git://github.com/nimrod-code") or + if pkg["url"].str.startsWith("https://github.com/nim-lang") or pkg["url"].str.startsWith("git://github.com/nim-lang") or "official" in pkg["tags"].elems: officialCount.inc |