diff options
460 files changed, 6141 insertions, 4263 deletions
diff --git a/.travis.yml b/.travis.yml index 6fb19648e..4c3c687e2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -42,7 +42,7 @@ before_script: - export PATH=$(pwd)/bin${PATH:+:$PATH} script: - nim c koch - - ./koch boot + - env NIM_COMPILE_TO_CPP=false ./koch boot - ./koch boot -d:release - ./koch nimble - nim e tests/test_nimscript.nims diff --git a/changelog.md b/changelog.md index 21b7b16a0..1a005639a 100644 --- a/changelog.md +++ b/changelog.md @@ -12,12 +12,19 @@ instead. - The OpenMP parallel iterator \``||`\` now supports any `#pragma omp directives` - and not just `#pragma omp parallel for`. See [OpenMP documentation](https://www.openmp.org/wp-content/uploads/OpenMP-4.5-1115-CPP-web.pdf). + and not just `#pragma omp parallel for`. See + [OpenMP documentation](https://www.openmp.org/wp-content/uploads/OpenMP-4.5-1115-CPP-web.pdf). The default annotation is `parallel for`, if you used OpenMP without annotation the change is transparent, if you used annotations you will have to prefix your previous annotations with `parallel for`. +- The `unchecked` pragma was removed, instead use `system.UncheckedArray`. +- The undocumented ``#? strongSpaces`` parsing mode has been removed. +- The `not` operator is now always a unary operator, this means that code like + ``assert not isFalse(3)`` compiles. + + #### Breaking changes in the standard library @@ -36,11 +43,16 @@ proc enumToString*(enums: openArray[enum]): string = result = newString(enums.len * 2) ``` +- ``discard x`` is now illegal when `x` is a function symbol. + ### Library additions -- There is a new stdlib module `editdistance` as a replacement for the +- There is a new stdlib module `std/editdistance` as a replacement for the deprecated `strutils.editDistance`. +- There is a new stdlib module `std/wordwrap` as a replacement for the + deprecated `strutils.wordwrap`. + - Added `split`, `splitWhitespace`, `size`, `alignLeft`, `align`, `strip`, `repeat` procs and iterators to `unicode.nim`. @@ -49,12 +61,30 @@ proc enumToString*(enums: openArray[enum]): string = - Added `system.typeof` for more control over how `type` expressions can be deduced. +- Added `macros.isInstantiationOf` for checking if the proc symbol + is instantiation of generic proc symbol. + + ### Library changes - The string output of `macros.lispRepr` proc has been tweaked slightly. The `dumpLisp` macro in this module now outputs an indented proper Lisp, devoid of commas. +- In `strutils` empty strings now no longer matched as substrings + anymore. + +- Complex type is now generic and not a tuple anymore. + +- The `ospaths` module is now deprecated, use `os` instead. Note that + `os` is available in a NimScript environment but unsupported + operations produce a compile-time error. + +- The `parseopt` module now supports a new flag `allowWhitespaceAfterColon` + (default value: true) that can be set to `false` for better Posix + interoperability. (Bug #9619.) + + ### Language additions - Vm suport for float32<->int32 and float64<->int64 casts was added. @@ -62,6 +92,11 @@ proc enumToString*(enums: openArray[enum]): string = ### Language changes +- The standard extension for SCF (source code filters) files was changed from + ``.tmpl`` to ``.nimf``, + it's more recognizable and allows tools like github to recognize it as Nim, + see [#9647](https://github.com/nim-lang/Nim/issues/9647). + The previous extension will continue to work. ### Tool changes - `jsondoc` now include a `moduleDescription` field with the module diff --git a/changelogs/changelog_0_19_0.md b/changelogs/changelog_0_19_0.md index 7464f50b6..18d3ca2b3 100644 --- a/changelogs/changelog_0_19_0.md +++ b/changelogs/changelog_0_19_0.md @@ -223,7 +223,7 @@ into the namespace "Nim" in order to avoid naming conflicts with existing C++ code. This is done for all Nim code - internal and exported. -- Added ``macros.getProjectPath`` and ``ospaths.putEnv`` procs to Nim's virtual +- Added ``macros.getProjectPath`` and ``os.putEnv`` procs to Nim's virtual machine. - The ``deadCodeElim`` option is now always turned on and the switch has no diff --git a/compiler/ast.nim b/compiler/ast.nim index 5fc8e5978..6a73df3c7 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -565,13 +565,9 @@ const routineKinds* = {skProc, skFunc, skMethod, skIterator, skConverter, skMacro, skTemplate} tfIncompleteStruct* = tfVarargs - tfUncheckedArray* = tfVarargs tfUnion* = tfNoSideEffect tfGcSafe* = tfThread tfObjHasKids* = tfEnumHasHoles - tfOldSchoolExprStmt* = tfVarargs # for now used to distinguish \ - # 'varargs[expr]' from 'varargs[untyped]'. Eventually 'expr' will be - # deprecated and this mess can be cleaned up. tfReturnsNew* = tfInheritable skError* = skUnknown @@ -660,7 +656,8 @@ type mNHint, mNWarning, mNError, mInstantiationInfo, mGetTypeInfo, mNimvm, mIntDefine, mStrDefine, mRunnableExamples, - mException, mBuiltinType, mSymOwner, mUncheckedArray, mGetImplTransf + mException, mBuiltinType, mSymOwner, mUncheckedArray, mGetImplTransf, + mSymIsInstantiationOf # things that we can evaluate safely at compile time, even if not asked for it: const diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 6678a07ca..388ab806e 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -173,21 +173,6 @@ proc genRefAssign(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src)) elif dest.storage == OnHeap: # location is on heap - # now the writer barrier is inlined for performance: - # - # if afSrcIsNotNil in flags: - # UseMagic(p.module, 'nimGCref') - # lineF(p, cpsStmts, 'nimGCref($1);$n', [rdLoc(src)]) - # elif afSrcIsNil notin flags: - # UseMagic(p.module, 'nimGCref') - # lineF(p, cpsStmts, 'if ($1) nimGCref($1);$n', [rdLoc(src)]) - # if afDestIsNotNil in flags: - # UseMagic(p.module, 'nimGCunref') - # lineF(p, cpsStmts, 'nimGCunref($1);$n', [rdLoc(dest)]) - # elif afDestIsNil notin flags: - # UseMagic(p.module, 'nimGCunref') - # lineF(p, cpsStmts, 'if ($1) nimGCunref($1);$n', [rdLoc(dest)]) - # lineF(p, cpsStmts, '$1 = $2;$n', [rdLoc(dest), rdLoc(src)]) if canFormAcycle(dest.t): linefmt(p, cpsStmts, "#asgnRef((void**) $1, $2);$n", addrLoc(p.config, dest), rdLoc(src)) @@ -872,7 +857,7 @@ proc genArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) = var ty = skipTypes(a.t, abstractVarRange + abstractPtrs + tyUserTypeClasses) var first = intLiteral(firstOrd(p.config, ty)) # emit range check: - if optBoundsCheck in p.options and tfUncheckedArray notin ty.flags: + if optBoundsCheck in p.options and ty.kind != tyUncheckedArray: if not isConstExpr(y): # semantic pass has already checked for const index expressions if firstOrd(p.config, ty) == 0: @@ -909,11 +894,10 @@ proc genBoundsCheck(p: BProc; arr, a, b: TLoc) = rdLoc(a), rdLoc(b), rdLoc(arr)) of tyArray: let first = intLiteral(firstOrd(p.config, ty)) - if tfUncheckedArray notin ty.flags: - linefmt(p, cpsStmts, - "if ($2-$1 != -1 && " & - "($2-$1 < -1 || $1 < $3 || $1 > $4 || $2 < $3 || $2 > $4)) #raiseIndexError();$n", - rdCharLoc(a), rdCharLoc(b), first, intLiteral(lastOrd(p.config, ty))) + linefmt(p, cpsStmts, + "if ($2-$1 != -1 && " & + "($2-$1 < -1 || $1 < $3 || $1 > $4 || $2 < $3 || $2 > $4)) #raiseIndexError();$n", + rdCharLoc(a), rdCharLoc(b), first, intLiteral(lastOrd(p.config, ty))) of tySequence, tyString: linefmt(p, cpsStmts, "if ($2-$1 != -1 && " & @@ -1156,7 +1140,7 @@ proc genSeqElemAppend(p: BProc, e: PNode, d: var TLoc) = getIntTemp(p, tmpL) lineCg(p, cpsStmts, "$1 = $2->$3++;$n", tmpL.r, rdLoc(a), lenField(p)) dest.r = ropecg(p.module, "$1$3[$2]", rdLoc(a), tmpL.r, dataField(p)) - genAssignment(p, dest, b, {needToCopy, afDestIsNil}) + genAssignment(p, dest, b, {needToCopy}) gcUsage(p.config, e) proc genReset(p: BProc, n: PNode) = @@ -1395,7 +1379,7 @@ proc genArrToSeq(p: BProc, n: PNode, d: var TLoc) = elem.storage = OnHeap # we know that sequences are on the heap initLoc(arr, locExpr, lodeTyp elemType(skipTypes(n.sons[1].typ, abstractInst)), a.storage) arr.r = ropecg(p.module, "$1[$2]", rdLoc(a), intLiteral(i)) - genAssignment(p, elem, arr, {afDestIsNil, needToCopy}) + genAssignment(p, elem, arr, {needToCopy}) else: var i: TLoc getTemp(p, getSysType(p.module.g.graph, unknownLineInfo(), tyInt), i) @@ -1406,7 +1390,7 @@ proc genArrToSeq(p: BProc, n: PNode, d: var TLoc) = elem.storage = OnHeap # we know that sequences are on the heap initLoc(arr, locExpr, lodeTyp elemType(skipTypes(n.sons[1].typ, abstractInst)), a.storage) arr.r = ropecg(p.module, "$1[$2]", rdLoc(a), rdLoc(i)) - genAssignment(p, elem, arr, {afDestIsNil, needToCopy}) + genAssignment(p, elem, arr, {needToCopy}) lineF(p, cpsStmts, "}$n", []) @@ -1581,8 +1565,17 @@ proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) = else: putIntoDest(p, d, e, rope(lengthOrd(p.config, typ))) else: internalError(p.config, e.info, "genArrayLen()") +proc makePtrType(baseType: PType): PType = + result = newType(tyPtr, baseType.owner) + addSonSkipIntLit(result, baseType) + +proc makeAddr(n: PNode): PNode = + result = newTree(nkHiddenAddr, n) + result.typ = makePtrType(n.typ) + proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc) = if p.config.selectedGc == gcDestructors: + e.sons[1] = makeAddr(e[1]) genCall(p, e, d) return var a, b, call: TLoc @@ -1964,6 +1957,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of mAppendStrStr: genStrAppend(p, e, d) of mAppendSeqElem: if p.config.selectedGc == gcDestructors: + e.sons[1] = makeAddr(e[1]) genCall(p, e, d) else: genSeqElemAppend(p, e, d) @@ -2031,8 +2025,11 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = let n = lowerings.wrapProcForSpawn(p.module.g.graph, p.module.module, e, e.typ, nil, nil) expr(p, n, d) of mParallel: - let n = semparallel.liftParallel(p.module.g.graph, p.module.module, e) - expr(p, n, d) + when defined(leanCompiler): + quit "compiler built without support for the 'parallel' statement" + else: + let n = semparallel.liftParallel(p.module.g.graph, p.module.module, e) + expr(p, n, d) of mDeepCopy: var a, b: TLoc let x = if e[1].kind in {nkAddr, nkHiddenAddr}: e[1][0] else: e[1] @@ -2429,15 +2426,8 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = if ex.kind != nkEmpty: genLineDir(p, n) var a: TLoc - 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, ex, a) - if lfSingleUse notin a.flags: - line(p, cpsStmts, a.r & ";\L") - else: - initLocExpr(p, ex, a) + initLocExprSingleUse(p, ex, a) + line(p, cpsStmts, "(void)(" & a.r & ");\L") of nkAsmStmt: genAsmStmt(p, n) of nkTryStmt: if p.module.compileToCpp and optNoCppExceptions notin p.config.globalOptions: diff --git a/compiler/ccgliterals.nim b/compiler/ccgliterals.nim index 34677ec06..ccfa49a1d 100644 --- a/compiler/ccgliterals.nim +++ b/compiler/ccgliterals.nim @@ -69,7 +69,7 @@ proc genStringLiteralV2(m: BModule; n: PNode): Rope = addf(m.s[cfsData], "static const NimStringV2 $1 = {$2, (NimStrPayload*)&$3};$n", [result, rope(len(n.strVal)), pureLit]) else: - result = m.tmpBase & rope(id) + result = m.tmpBase & rope(id+1) proc genStringLiteralV2Const(m: BModule; n: PNode): Rope = let id = nodeTableTestOrSet(m.dataCache, n, m.labels) diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index 5ebe0323d..3665a7e85 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -509,7 +509,7 @@ proc genWhileStmt(p: BProc, t: PNode) = # for closure support weird loop bodies are generated: if loopBody.len == 2 and loopBody.sons[0].kind == nkEmpty: loopBody = loopBody.sons[1] - genComputedGoto(p, loopBody) # TODO foobar + genComputedGoto(p, loopBody) else: p.breakIdx = startBlock(p, "while (1) {$n") p.blocks[p.breakIdx].isLoop = true @@ -874,14 +874,15 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) = discard pop(p.nestedTryStmts) - if not catchAllPresent and t[^1].kind == nkFinally: - # finally requires catch all presence - startBlock(p, "catch (...) {$n") - genSimpleBlock(p, t[^1][0]) - line(p, cpsStmts, ~"throw;$n") - endBlock(p) - if t[^1].kind == nkFinally: + # c++ does not have finally, therefore code needs to be generated twice + if not catchAllPresent: + # finally requires catch all presence + startBlock(p, "catch (...) {$n") + genStmts(p, t[^1][0]) + line(p, cpsStmts, ~"throw;$n") + endBlock(p) + genSimpleBlock(p, t[^1][0]) proc genTry(p: BProc, t: PNode, d: var TLoc) = diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 75566fb38..bbfd72354 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -492,7 +492,7 @@ proc genRecordFieldsAux(m: BModule, n: PNode, # with heavily templatized C++ code: if not isImportedCppType(rectype): let fieldType = field.loc.lode.typ.skipTypes(abstractInst) - if fieldType.kind == tyArray and tfUncheckedArray in fieldType.flags: + if fieldType.kind == tyUncheckedArray: addf(result, "$1 $2[SEQ_DECL_SIZE];$n", [getTypeDescAux(m, fieldType.elemType, check), sname]) elif fieldType.kind == tySequence and m.config.selectedGC != gcDestructors: @@ -1188,6 +1188,7 @@ proc genTypeInfo(m: BModule, t: PType; info: TLineInfo): Rope = let owner = t.skipTypes(typedescPtrs).owner.getModule if owner != m.module: # make sure the type info is created in the owner module + assert m.g.modules[owner.position] != nil discard genTypeInfo(m.g.modules[owner.position], origType, info) # reference the type info as extern here discard cgsym(m, "TNimType") @@ -1213,8 +1214,8 @@ proc genTypeInfo(m: BModule, t: PType; info: TLineInfo): Rope = let x = fakeClosureType(m, t.owner) genTupleInfo(m, x, x, result, info) of tySequence: + genTypeInfoAux(m, t, t, result, info) if m.config.selectedGC != gcDestructors: - genTypeInfoAux(m, t, t, result, info) if m.config.selectedGC >= gcMarkAndSweep: let markerProc = genTraverseProc(m, origType, sig) addf(m.s[cfsTypeInit3], "$1.marker = $2;$n", [result, markerProc]) diff --git a/compiler/cgen.nim b/compiler/cgen.nim index ab5584786..199a93be2 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -14,7 +14,10 @@ import nversion, nimsets, msgs, std / sha1, bitsets, idents, types, ccgutils, os, ropes, math, passes, wordrecg, treetab, cgmeth, condsyms, rodutils, renderer, idgen, cgendata, ccgmerge, semfold, aliases, - lowerings, semparallel, tables, sets, ndi, lineinfos, pathutils, transf + lowerings, tables, sets, ndi, lineinfos, pathutils, transf + +when not defined(leanCompiler): + import semparallel import strutils except `%` # collides with ropes.`%` @@ -42,8 +45,7 @@ when options.hasTinyCBackend: # implementation proc addForwardedProc(m: BModule, prc: PSym) = - m.forwardedProcs.add(prc) - inc(m.g.forwardedProcsCounter) + m.g.forwardedProcs.add(prc) proc findPendingModule(m: BModule, s: PSym): BModule = var ms = getModule(s) @@ -292,7 +294,7 @@ proc genObjectInit(p: BProc, section: TCProcSection, t: PType, a: TLoc, type TAssignmentFlag = enum - needToCopy, afDestIsNil, afDestIsNotNil, afSrcIsNil, afSrcIsNotNil + needToCopy TAssignmentFlags = set[TAssignmentFlag] proc genRefAssign(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) @@ -311,7 +313,7 @@ proc resetLoc(p: BProc, loc: var TLoc) = var nilLoc: TLoc initLoc(nilLoc, locTemp, loc.lode, OnStack) nilLoc.r = rope("NIM_NIL") - genRefAssign(p, loc, nilLoc, {afSrcIsNil}) + genRefAssign(p, loc, nilLoc, {}) else: linefmt(p, cpsStmts, "$1 = 0;$n", rdLoc(loc)) else: @@ -1391,7 +1393,6 @@ proc rawNewModule(g: BModuleList; module: PSym, filename: AbsoluteFile): BModule result.preInitProc = newPreInitProc(result) initNodeTable(result.dataCache) result.typeStack = @[] - result.forwardedProcs = @[] result.typeNodesName = getTempName(result) result.nimTypesName = getTempName(result) # no line tracing for the init sections of the system module so that we @@ -1407,49 +1408,6 @@ proc nullify[T](arr: var T) = for i in low(arr)..high(arr): arr[i] = Rope(nil) -proc resetModule*(m: BModule) = - # between two compilations in CAAS mode, we can throw - # away all the data that was written to disk - m.headerFiles = @[] - m.declaredProtos = initIntSet() - m.forwTypeCache = initTable[SigHash, Rope]() - m.initProc = newProc(nil, m) - m.initProc.options = initProcOptions(m) - m.preInitProc = newPreInitProc(m) - initNodeTable(m.dataCache) - m.typeStack = @[] - m.forwardedProcs = @[] - m.typeNodesName = getTempName(m) - m.nimTypesName = getTempName(m) - if sfSystemModule in m.module.flags: - incl m.flags, preventStackTrace - else: - excl m.flags, preventStackTrace - nullify m.s - m.typeNodes = 0 - m.nimTypes = 0 - nullify m.extensionLoaders - - # indicate that this is now cached module - # the cache will be invalidated by nullifying gModules - #m.fromCache = true - m.g = nil - - # we keep only the "merge info" information for the module - # and the properties that can't change: - # m.filename - # m.cfilename - # m.isHeaderFile - # m.module ? - # m.typeCache - # m.declaredThings - # m.typeInfoMarker - # m.labels - # m.FrameDeclared - -proc resetCgenModules*(g: BModuleList) = - for m in cgenModules(g): resetModule(m) - proc rawNewModule(g: BModuleList; module: PSym; conf: ConfigRef): BModule = result = rawNewModule(g, module, AbsoluteFile toFullPath(conf, module.position.FileIndex)) @@ -1528,20 +1486,6 @@ proc myProcess(b: PPassContext, n: PNode): PNode = let tranformed_n = transformStmt(m.g.graph, m.module, n) genStmts(m.initProc, tranformed_n) -proc finishModule(m: BModule) = - var i = 0 - while i <= high(m.forwardedProcs): - # Note: ``genProc`` may add to ``m.forwardedProcs``, so we cannot use - # a ``for`` loop here - var prc = m.forwardedProcs[i] - if sfForward in prc.flags: - internalError(m.config, prc.info, "still forwarded: " & prc.name.s) - genProcNoForward(m, prc) - inc(i) - assert(m.g.forwardedProcsCounter >= i) - dec(m.g.forwardedProcsCounter, i) - setLen(m.forwardedProcs, 0) - proc shouldRecompile(m: BModule; code: Rope, cfile: Cfile): bool = result = true if optForceFullMake notin m.config.globalOptions: @@ -1637,12 +1581,26 @@ proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode = registerModuleToMain(m.g, m.module) if sfMainModule in m.module.flags: - if m.g.forwardedProcsCounter == 0: + if m.g.forwardedProcs.len == 0: incl m.flags, objHasKidsValid let disp = generateMethodDispatchers(graph) for x in disp: genProcAux(m, x.sym) genMainProc(m) +proc genForwardedProcs(g: BModuleList) = + # Forward declared proc:s lack bodies when first encountered, so they're given + # a second pass here + # Note: ``genProcNoForward`` may add to ``forwardedProcs`` + while g.forwardedProcs.len > 0: + let + prc = g.forwardedProcs.pop() + ms = getModule(prc) + m = g.modules[ms.position] + if sfForward in prc.flags: + internalError(m.config, prc.info, "still forwarded: " & prc.name.s) + + genProcNoForward(m, prc) + proc cgenWriteModules*(backend: RootRef, config: ConfigRef) = let g = BModuleList(backend) # we need to process the transitive closure because recursive module @@ -1652,10 +1610,9 @@ proc cgenWriteModules*(backend: RootRef, config: ConfigRef) = let (outDir, _, _) = splitFile(config.outfile) if not outDir.isEmpty: createDir(outDir) - if g.generatedHeader != nil: finishModule(g.generatedHeader) - while g.forwardedProcsCounter > 0: - for m in cgenModules(g): - finishModule(m) + + genForwardedProcs(g) + for m in cgenModules(g): m.writeModule(pending=true) writeMapping(config, g.mapping) diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim index 0c6097fbe..28e36364e 100644 --- a/compiler/cgendata.nim +++ b/compiler/cgendata.nim @@ -112,7 +112,7 @@ type mainModProcs*, mainModInit*, otherModsInit*, mainDatInit*: Rope mapping*: Rope # the generated mapping file (if requested) modules*: seq[BModule] # list of all compiled modules - forwardedProcsCounter*: int + forwardedProcs*: seq[PSym] # proc:s that did not yet have a body generatedHeader*: BModule breakPointId*: int breakpoints*: Rope # later the breakpoints are inserted into the main proc @@ -150,7 +150,6 @@ type preInitProc*: BProc # code executed before the init proc typeStack*: TTypeSeq # used for type generation dataCache*: TNodeTable - forwardedProcs*: TSymSeq # keep forwarded procs here typeNodes*, nimTypes*: int # used for type info generation typeNodesName*, nimTypesName*: Rope # used for type info generation labels*: Natural # for generating unique module-scope names @@ -188,12 +187,12 @@ proc newProc*(prc: PSym, module: BModule): BProc = result.sigConflicts = initCountTable[string]() proc newModuleList*(g: ModuleGraph): BModuleList = - BModuleList(modules: @[], typeInfoMarker: initTable[SigHash, Rope](), config: g.config, - graph: g, nimtvDeps: @[], nimtvDeclared: initIntSet()) + BModuleList(typeInfoMarker: initTable[SigHash, Rope](), config: g.config, + graph: g, nimtvDeclared: initIntSet()) iterator cgenModules*(g: BModuleList): BModule = - for i in 0..high(g.modules): + for m in g.modules: # ultimately, we are iterating over the file ids here. # some "files" won't have an associated cgen module (like stdin) # and we must skip over them. - if g.modules[i] != nil: yield g.modules[i] + if m != nil: yield m diff --git a/compiler/closureiters.nim b/compiler/closureiters.nim index 258372d76..c2d908193 100644 --- a/compiler/closureiters.nim +++ b/compiler/closureiters.nim @@ -462,10 +462,17 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode = result.typ = n.typ for i in 0 ..< n.len: - if n[i].kind == nkStmtListExpr: + case n[i].kind + of nkExprColonExpr: + if n[i][1].kind == nkStmtListExpr: + let (st, ex) = exprToStmtList(n[i][1]) + result.add(st) + n[i][1] = ex + of nkStmtListExpr: let (st, ex) = exprToStmtList(n[i]) result.add(st) n[i] = ex + else: discard result.add(n) of nkIfStmt, nkIfExpr: @@ -852,15 +859,8 @@ proc transformClosureIteratorBody(ctx: var Ctx, n: PNode, gotoOut: PNode): PNode discard of nkStmtList, nkStmtListExpr: - assert(isEmptyType(n.typ), "nkStmtListExpr not lowered") - result = addGotoOut(result, gotoOut) for i in 0 ..< n.len: - if n[i].hasYieldsInExpressions: - # Lower nkStmtListExpr nodes inside `n[i]` first - var ns = false - n[i] = ctx.lowerStmtListExprs(n[i], ns) - if n[i].hasYields: # Create a new split let go = newNodeI(nkGotoState, n[i].info) @@ -1294,11 +1294,17 @@ proc transformClosureIterator*(g: ModuleGraph; fn: PSym, n: PNode): PNode = ctx.stateVarSym.typ = g.createClosureIterStateType(fn) ctx.stateLoopLabel = newSym(skLabel, getIdent(ctx.g.cache, ":stateLoop"), fn, fn.info) - let n = n.toStmtList + var n = n.toStmtList discard ctx.newState(n, nil) let gotoOut = newTree(nkGotoState, g.newIntLit(n.info, -1)) + var ns = false + n = ctx.lowerStmtListExprs(n, ns) + + if n.hasYieldsInExpressions(): + internalError(ctx.g.config, "yield in expr not lowered") + # Splitting transformation discard ctx.transformClosureIteratorBody(n, gotoOut) diff --git a/compiler/commands.nim b/compiler/commands.nim index b39cc0b72..fa17e9851 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -642,14 +642,19 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; of "help", "h": expectNoArg(conf, switch, arg, pass, info) helpOnError(conf, pass) - of "symbolfiles", "incremental": + of "symbolfiles": discard "ignore for backwards compat" + of "incremental": + when not defined(nimIncremental): + localError(conf, info, "the compiler was not built with " & + "incremental compilation features; bootstrap with " & + "-d:nimIncremental to enable") case arg.normalize of "on": conf.symbolFiles = v2Sf of "off": conf.symbolFiles = disabledSf of "writeonly": conf.symbolFiles = writeOnlySf of "readonly": conf.symbolFiles = readOnlySf of "v2": conf.symbolFiles = v2Sf - else: localError(conf, info, "invalid option for --symbolFiles: " & arg) + else: localError(conf, info, "invalid option for --incremental: " & arg) of "skipcfg": expectNoArg(conf, switch, arg, pass, info) incl(conf.globalOptions, optSkipSystemConfigFile) @@ -739,6 +744,8 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; of "hint": conf.globalOptions = conf.globalOptions + {optStyleHint} of "error": conf.globalOptions = conf.globalOptions + {optStyleError} else: localError(conf, info, errOffHintsError % arg) + of "showallmismatches": + processOnOffSwitchG(conf, {optShowAllMismatches}, arg, pass, info) of "cppcompiletonamespace": if arg.len > 0: conf.cppCustomNamespace = arg @@ -774,6 +781,7 @@ proc processArgument*(pass: TCmdLinePass; p: OptParser; # nim filename.nims is the same as "nim e filename.nims": if p.key.endswith(".nims"): config.command = "e" + incl(config.globalOptions, optWasNimscript) config.projectName = unixToNativePath(p.key) config.arguments = cmdLineRest(p) result = true diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index 648dcd8c4..9a4c1701c 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -85,7 +85,8 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimHasUserErrors") defineSymbol("nimUncheckedArrayTyp") defineSymbol("nimHasTypeof") - + defineSymbol("nimErrorProcCanHaveBody") + defineSymbol("nimHasInstantiationOfInMacro") defineSymbol("nimHasNilSeqs") for f in low(Feature)..high(Feature): defineSymbol("nimHas" & $f) diff --git a/compiler/destroyer.nim b/compiler/destroyer.nim index b621e99b9..c515ba1d4 100644 --- a/compiler/destroyer.nim +++ b/compiler/destroyer.nim @@ -261,6 +261,11 @@ proc isLastRead(n: PNode; c: var Con): bool = template interestingSym(s: PSym): bool = s.owner == c.owner and s.kind in InterestingSyms and hasDestructor(s.typ) +template isUnpackedTuple(s: PSym): bool = + ## we move out all elements of unpacked tuples, + ## hence unpacked tuples themselves don't need to be destroyed + s.kind == skTemp and s.typ.kind == tyTuple + proc patchHead(n: PNode) = if n.kind in nkCallKinds and n[0].kind == nkSym and n.len > 1: let s = n[0].sym @@ -446,6 +451,13 @@ proc moveOrCopy(dest, ri: PNode; c: var Con): PNode = ri2.add pArg(ri[i], c, i < L and parameters[i].kind == tySink) #recurse(ri, ri2) result.add ri2 + of nkBracketExpr: + if ri[0].kind == nkSym and isUnpackedTuple(ri[0].sym): + # unpacking of tuple: move out the elements + result = genSink(c, dest.typ, dest, ri) + else: + result = genCopy(c, dest.typ, dest, ri) + result.add p(ri, c) of nkObjConstr: result = genSink(c, dest.typ, dest, ri) let ri2 = copyTree(ri) @@ -454,6 +466,17 @@ proc moveOrCopy(dest, ri: PNode; c: var Con): PNode = # so these all act like 'sink' parameters: ri2[i].sons[1] = pArg(ri[i][1], c, isSink = true) result.add ri2 + of nkTupleConstr: + result = genSink(c, dest.typ, dest, ri) + let ri2 = copyTree(ri) + for i in 0..<ri.len: + # everything that is passed to an tuple constructor is consumed, + # so these all act like 'sink' parameters: + if ri[i].kind == nkExprColonExpr: + ri2[i].sons[1] = pArg(ri[i][1], c, isSink = true) + else: + ri2[i] = pArg(ri[i], c, isSink = true) + result.add ri2 of nkSym: if ri.sym.kind != skParam and isLastRead(ri, c): # Rule 3: `=sink`(x, z); wasMoved(z) @@ -483,7 +506,7 @@ proc p(n: PNode; c: var Con): PNode = if it.kind == nkVarTuple and hasDestructor(ri.typ): let x = lowerTupleUnpacking(c.graph, it, c.owner) result.add p(x, c) - elif it.kind == nkIdentDefs and hasDestructor(it[0].typ): + elif it.kind == nkIdentDefs and hasDestructor(it[0].typ) and not isUnpackedTuple(it[0].sym): for j in 0..L-2: let v = it[j] doAssert v.kind == nkSym diff --git a/compiler/dfa.nim b/compiler/dfa.nim index 4b624e93b..415cc4ab3 100644 --- a/compiler/dfa.nim +++ b/compiler/dfa.nim @@ -56,6 +56,7 @@ type inCall, inTryStmt: int blocks: seq[TBlock] tryStmtFixups: seq[TPosition] + owner: PSym proc debugInfo(info: TLineInfo): string = result = $info.line #info.toFilename & ":" & $info.line @@ -259,8 +260,15 @@ proc genRaise(c: var Con; n: PNode) = else: c.code.add Instr(n: n, kind: goto, dest: high(int) - c.code.len) +proc genImplicitReturn(c: var Con) = + if c.owner.kind in {skProc, skFunc, skMethod, skIterator, skConverter} and resultPos < c.owner.ast.len: + gen(c, c.owner.ast.sons[resultPos]) + proc genReturn(c: var Con; n: PNode) = - if n.sons[0].kind != nkEmpty: gen(c, n.sons[0]) + if n.sons[0].kind != nkEmpty: + gen(c, n.sons[0]) + else: + genImplicitReturn(c) c.code.add Instr(n: n, kind: goto, dest: high(int) - c.code.len) const @@ -461,11 +469,13 @@ proc dfa(code: seq[Instr]; conf: ConfigRef) = proc dataflowAnalysis*(s: PSym; body: PNode; conf: ConfigRef) = var c = Con(code: @[], blocks: @[]) gen(c, body) + genImplicitReturn(c) when defined(useDfa) and defined(debugDfa): echoCfg(c.code) dfa(c.code, conf) proc constructCfg*(s: PSym; body: PNode): ControlFlowGraph = ## constructs a control flow graph for ``body``. - var c = Con(code: @[], blocks: @[]) + var c = Con(code: @[], blocks: @[], owner: s) gen(c, body) + genImplicitReturn(c) shallowCopy(result, c.code) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index f5795e1d2..6f61d020d 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -897,7 +897,7 @@ proc genOutFile(d: PDoc): Rope = setIndexTerm(d[], external, "", title) else: # Modules get an automatic title for the HTML, but no entry in the index. - title = "Module " & extractFilename(changeFileExt(d.filename, "")) + title = extractFilename(changeFileExt(d.filename, "")) let bodyname = if d.hasToc and not d.isPureRst: "doc.body_toc_group" elif d.hasToc: "doc.body_toc" diff --git a/compiler/importer.nim b/compiler/importer.nim index 60b7872fe..131b1ad8a 100644 --- a/compiler/importer.nim +++ b/compiler/importer.nim @@ -100,7 +100,7 @@ proc importAllSymbolsExcept(c: PContext, fromMod: PSym, exceptSet: IntSet) = if s.kind != skModule: if s.kind != skEnumField: if s.kind notin ExportableSymKinds: - internalError(c.config, s.info, "importAllSymbols: " & $s.kind) + internalError(c.config, s.info, "importAllSymbols: " & $s.kind & " " & s.name.s) if exceptSet.isNil or s.name.id notin exceptSet: rawImportSymbol(c, s) s = nextIter(i, fromMod.tab) diff --git a/compiler/incremental.nim b/compiler/incremental.nim index 47637b3c1..f66a75efd 100644 --- a/compiler/incremental.nim +++ b/compiler/incremental.nim @@ -59,8 +59,8 @@ when nimIncremental: let id = row[0] let fullhash = hashFileCached(conf, fileIdx, AbsoluteFile fullpath) if id.len == 0: - result = int incr.db.insertID(sql"insert into filenames(fullpath, fullhash) values (?, ?)", - fullpath, fullhash) + result = int incr.db.insertID(sql"insert into filenames(nimid, fullpath, fullhash) values (?, ?, ?)", + int(fileIdx), fullpath, fullhash) else: if row[1] != fullhash: incr.db.exec(sql"update filenames set fullhash = ? where fullpath = ?", fullhash, fullpath) @@ -102,6 +102,7 @@ when nimIncremental: db.exec(sql""" create table if not exists filenames( id integer primary key, + nimid integer not null, fullpath varchar(8000) not null, fullHash varchar(256) not null ); diff --git a/compiler/layouter.nim b/compiler/layouter.nim index f96f88da1..8605ade45 100644 --- a/compiler/layouter.nim +++ b/compiler/layouter.nim @@ -30,7 +30,7 @@ type lastTok: TTokType inquote, lastTokWasTerse: bool semicolons: SemicolonKind - col, lastLineNumber, lineSpan, indentLevel, indWidth: int + col, lastLineNumber, lineSpan, indentLevel, indWidth*: int keepIndents*: int doIndentMore*: int content: string @@ -41,9 +41,10 @@ type proc openEmitter*(em: var Emitter, cache: IdentCache; config: ConfigRef, fileIdx: FileIndex) = let fullPath = Absolutefile config.toFullPath(fileIdx) - em.indWidth = getIndentWidth(fileIdx, llStreamOpen(fullPath, fmRead), - cache, config) - if em.indWidth == 0: em.indWidth = 2 + if em.indWidth == 0: + em.indWidth = getIndentWidth(fileIdx, llStreamOpen(fullPath, fmRead), + cache, config) + if em.indWidth == 0: em.indWidth = 2 em.config = config em.fid = fileIdx em.lastTok = tkInvalid @@ -245,7 +246,7 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) = wr(TokTypeToStr[tok.tokType]) if not em.inquote: wr(" ") of tkOpr, tkDotDot: - if tok.strongSpaceA == 0 and tok.strongSpaceB == 0: + if (tok.strongSpaceA == 0 and tok.strongSpaceB == 0) or em.inquote: # bug #9504: remember to not spacify a keyword: lastTokWasTerse = true # if not surrounded by whitespace, don't produce any whitespace either: diff --git a/compiler/lexer.nim b/compiler/lexer.nim index 877369c2a..635e6f08d 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -635,7 +635,8 @@ proc handleHexChar(L: var TLexer, xi: var int) = inc(L.bufpos) else: lexMessage(L, errGenerated, - "expected a hex digit, but found: " & L.buf[L.bufpos]) + "expected a hex digit, but found: " & L.buf[L.bufpos] & + " ; maybe prepend with 0") # Need to progress for `nim check` inc(L.bufpos) @@ -966,7 +967,7 @@ proc getPrecedence*(tok: TToken, strongSpaces: bool): int = of '?': result = 2 else: considerAsgn(2) of tkDiv, tkMod, tkShl, tkShr: result = 9 - of tkIn, tkNotin, tkIs, tkIsnot, tkNot, tkOf, tkAs: result = 5 + of tkIn, tkNotin, tkIs, tkIsnot, tkOf, tkAs: result = 5 of tkDotDot: result = 6 of tkAnd: result = 4 of tkOr, tkXor, tkPtr, tkRef: result = 3 diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim index cd160313c..b1ecf779e 100644 --- a/compiler/lineinfos.nim +++ b/compiler/lineinfos.nim @@ -41,7 +41,7 @@ type hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded, hintConvFromXtoItselfNotNeeded, hintExprAlwaysX, hintQuitCalled, hintProcessing, hintCodeBegin, hintCodeEnd, hintConf, hintPath, - hintConditionAlwaysTrue, hintName, hintPattern, + hintConditionAlwaysTrue, hintConditionAlwaysFalse, hintName, hintPattern, hintExecuting, hintLinking, hintDependency, hintSource, hintPerformance, hintStackTrace, hintGCStats, hintGlobalVar, @@ -107,6 +107,7 @@ const hintConf: "used config file '$1'", hintPath: "added path: '$1'", hintConditionAlwaysTrue: "condition is always true: '$1'", + hintConditionAlwaysFalse: "condition is always false: '$1'", hintName: "name should be: '$1'", hintPattern: "$1", hintExecuting: "$1", @@ -140,7 +141,7 @@ const "Success", "SuccessX", "CC", "LineTooLong", "XDeclaredButNotUsed", "ConvToBaseNotNeeded", "ConvFromXtoItselfNotNeeded", "ExprAlwaysX", "QuitCalled", "Processing", "CodeBegin", "CodeEnd", "Conf", - "Path", "CondTrue", "Name", "Pattern", "Exec", "Link", "Dependency", + "Path", "CondTrue", "CondFalse", "Name", "Pattern", "Exec", "Link", "Dependency", "Source", "Performance", "StackTrace", "GCStats", "GlobalVar", "User", "UserRaw", "ExtendedContext", ] diff --git a/compiler/lookups.nim b/compiler/lookups.nim index d2e7fdcfa..2fb4e5241 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -168,7 +168,7 @@ proc ensureNoMissingOrUnusedSymbols(c: PContext; scope: PScope) = localError(c.config, s.info, "implementation of '$1' expected" % getSymRepr(c.config, s)) inc missingImpls - elif {sfUsed, sfExported} * s.flags == {} and optHints in s.options: + elif {sfUsed, sfExported} * s.flags == {}: if s.kind notin {skForVar, skParam, skMethod, skUnknown, skGenericParam}: # XXX: implicit type params are currently skTypes # maybe they can be made skGenericParam as well. diff --git a/compiler/main.nim b/compiler/main.nim index 6c8b0343e..6afe57d87 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -15,12 +15,15 @@ when not defined(nimcore): import llstream, strutils, ast, astalgo, lexer, syntaxes, renderer, options, msgs, os, condsyms, times, - wordrecg, sem, semdata, idents, passes, docgen, extccomp, - cgen, jsgen, json, nversion, + wordrecg, sem, semdata, idents, passes, extccomp, + cgen, json, nversion, platform, nimconf, importer, passaux, depends, vm, vmdef, types, idgen, - docgen2, parser, modules, ccgutils, sigmatch, ropes, + parser, modules, ccgutils, sigmatch, ropes, modulegraphs, tables, rod, lineinfos, pathutils +when not defined(leanCompiler): + import jsgen, docgen, docgen2 + from magicsys import resetSysTypes proc codegenPass(g: ModuleGraph) = @@ -57,13 +60,14 @@ proc commandCheck(graph: ModuleGraph) = semanticPasses(graph) # use an empty backend for semantic checking only compileProject(graph) -proc commandDoc2(graph: ModuleGraph; json: bool) = - graph.config.errorMax = high(int) # do not stop after first error - semanticPasses(graph) - if json: registerPass(graph, docgen2JsonPass) - else: registerPass(graph, docgen2Pass) - compileProject(graph) - finishDoc2Pass(graph.config.projectName) +when not defined(leanCompiler): + proc commandDoc2(graph: ModuleGraph; json: bool) = + graph.config.errorMax = high(int) # do not stop after first error + semanticPasses(graph) + if json: registerPass(graph, docgen2JsonPass) + else: registerPass(graph, docgen2Pass) + compileProject(graph) + finishDoc2Pass(graph.config.projectName) proc commandCompileToC(graph: ModuleGraph) = let conf = graph.config @@ -84,15 +88,16 @@ proc commandJsonScript(graph: ModuleGraph) = let proj = changeFileExt(graph.config.projectFull, "") extccomp.runJsonBuildInstructions(graph.config, proj) -proc commandCompileToJS(graph: ModuleGraph) = - #incl(gGlobalOptions, optSafeCode) - setTarget(graph.config.target, osJS, cpuJS) - #initDefines() - defineSymbol(graph.config.symbols, "ecmascript") # For backward compatibility - defineSymbol(graph.config.symbols, "js") - semanticPasses(graph) - registerPass(graph, JSgenPass) - compileProject(graph) +when not defined(leanCompiler): + proc commandCompileToJS(graph: ModuleGraph) = + #incl(gGlobalOptions, optSafeCode) + setTarget(graph.config.target, osJS, cpuJS) + #initDefines() + defineSymbol(graph.config.symbols, "ecmascript") # For backward compatibility + defineSymbol(graph.config.symbols, "js") + semanticPasses(graph) + registerPass(graph, JSgenPass) + compileProject(graph) proc interactivePasses(graph: ModuleGraph) = initDefines(graph.config.symbols) @@ -177,49 +182,76 @@ proc mainCommand*(graph: ModuleGraph) = else: rawMessage(conf, errGenerated, "'run' command not available; rebuild with -d:tinyc") of "js", "compiletojs": - conf.cmd = cmdCompileToJS - commandCompileToJS(graph) + when defined(leanCompiler): + quit "compiler wasn't built with JS code generator" + else: + conf.cmd = cmdCompileToJS + commandCompileToJS(graph) of "doc0": - wantMainModule(conf) - conf.cmd = cmdDoc - loadConfigs(DocConfig, cache, conf) - commandDoc(cache, conf) + when defined(leanCompiler): + quit "compiler wasn't built with documentation generator" + else: + wantMainModule(conf) + conf.cmd = cmdDoc + loadConfigs(DocConfig, cache, conf) + commandDoc(cache, conf) of "doc2", "doc": - conf.cmd = cmdDoc - loadConfigs(DocConfig, cache, conf) - defineSymbol(conf.symbols, "nimdoc") - commandDoc2(graph, false) + when defined(leanCompiler): + quit "compiler wasn't built with documentation generator" + else: + conf.cmd = cmdDoc + loadConfigs(DocConfig, cache, conf) + defineSymbol(conf.symbols, "nimdoc") + commandDoc2(graph, false) of "rst2html": - conf.cmd = cmdRst2html - loadConfigs(DocConfig, cache, conf) - commandRst2Html(cache, conf) + when defined(leanCompiler): + quit "compiler wasn't built with documentation generator" + else: + conf.cmd = cmdRst2html + loadConfigs(DocConfig, cache, conf) + commandRst2Html(cache, conf) of "rst2tex": - conf.cmd = cmdRst2tex - loadConfigs(DocTexConfig, cache, conf) - commandRst2TeX(cache, conf) + when defined(leanCompiler): + quit "compiler wasn't built with documentation generator" + else: + conf.cmd = cmdRst2tex + loadConfigs(DocTexConfig, cache, conf) + commandRst2TeX(cache, conf) of "jsondoc0": - wantMainModule(conf) - conf.cmd = cmdDoc - loadConfigs(DocConfig, cache, conf) - wantMainModule(conf) - defineSymbol(conf.symbols, "nimdoc") - commandJson(cache, conf) + when defined(leanCompiler): + quit "compiler wasn't built with documentation generator" + else: + wantMainModule(conf) + conf.cmd = cmdDoc + loadConfigs(DocConfig, cache, conf) + wantMainModule(conf) + defineSymbol(conf.symbols, "nimdoc") + commandJson(cache, conf) of "jsondoc2", "jsondoc": - conf.cmd = cmdDoc - loadConfigs(DocConfig, cache, conf) - wantMainModule(conf) - defineSymbol(conf.symbols, "nimdoc") - commandDoc2(graph, true) + when defined(leanCompiler): + quit "compiler wasn't built with documentation generator" + else: + conf.cmd = cmdDoc + loadConfigs(DocConfig, cache, conf) + wantMainModule(conf) + defineSymbol(conf.symbols, "nimdoc") + commandDoc2(graph, true) of "ctags": - wantMainModule(conf) - conf.cmd = cmdDoc - loadConfigs(DocConfig, cache, conf) - defineSymbol(conf.symbols, "nimdoc") - commandTags(cache, conf) + when defined(leanCompiler): + quit "compiler wasn't built with documentation generator" + else: + wantMainModule(conf) + conf.cmd = cmdDoc + loadConfigs(DocConfig, cache, conf) + defineSymbol(conf.symbols, "nimdoc") + commandTags(cache, conf) of "buildindex": - conf.cmd = cmdDoc - loadConfigs(DocConfig, cache, conf) - commandBuildIndex(cache, conf) + when defined(leanCompiler): + quit "compiler wasn't built with documentation generator" + else: + conf.cmd = cmdDoc + loadConfigs(DocConfig, cache, conf) + commandBuildIndex(cache, conf) of "gendepend": conf.cmd = cmdGenDepend commandGenDepend(graph) @@ -265,6 +297,7 @@ proc mainCommand*(graph: ModuleGraph) = conf.cmd = cmdInteractive commandInteractive(graph) of "e": + incl conf.globalOptions, optWasNimscript commandEval(graph, mainCommandArg(conf)) of "nop", "help": # prevent the "success" message: diff --git a/compiler/modulegraphs.nim b/compiler/modulegraphs.nim index 16704fab6..d05b301ae 100644 --- a/compiler/modulegraphs.nim +++ b/compiler/modulegraphs.nim @@ -63,6 +63,9 @@ type cacheCounters*: Table[string, BiggestInt] cacheTables*: Table[string, BTree[string, PNode]] passes*: seq[TPass] + onDefinition*: proc (graph: ModuleGraph; s: PSym; info: TLineInfo) {.nimcall.} + onDefinitionResolveForward*: proc (graph: ModuleGraph; s: PSym; info: TLineInfo) {.nimcall.} + onUsage*: proc (graph: ModuleGraph; s: PSym; info: TLineInfo) {.nimcall.} TPassContext* = object of RootObj # the pass's context PPassContext* = ref TPassContext @@ -78,6 +81,32 @@ type proc hash*(x: FileIndex): Hash {.borrow.} +when defined(nimfind): + template onUse*(info: TLineInfo; s: PSym) = + when compiles(c.c.graph): + if c.c.graph.onUsage != nil: c.c.graph.onUsage(c.c.graph, s, info) + else: + if c.graph.onUsage != nil: c.graph.onUsage(c.graph, s, info) + + template onDef*(info: TLineInfo; s: PSym) = + when compiles(c.c.graph): + if c.c.graph.onDefinition != nil: c.c.graph.onDefinition(c.c.graph, s, info) + else: + if c.graph.onDefinition != nil: c.graph.onDefinition(c.graph, s, info) + + template onDefResolveForward*(info: TLineInfo; s: PSym) = + when compiles(c.c.graph): + if c.c.graph.onDefinitionResolveForward != nil: + c.c.graph.onDefinitionResolveForward(c.c.graph, s, info) + else: + if c.graph.onDefinitionResolveForward != nil: + c.graph.onDefinitionResolveForward(c.graph, s, info) + +else: + template onUse*(info: TLineInfo; s: PSym) = discard + template onDef*(info: TLineInfo; s: PSym) = discard + template onDefResolveForward*(info: TLineInfo; s: PSym) = discard + proc stopCompile*(g: ModuleGraph): bool {.inline.} = result = g.doStopCompile != nil and g.doStopCompile() diff --git a/compiler/modules.nim b/compiler/modules.nim index 75e95e453..e2f322561 100644 --- a/compiler/modules.nim +++ b/compiler/modules.nim @@ -67,6 +67,7 @@ proc compileModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymFlags): P graph.config.mainPackageId = result.owner.id result.id = getModuleId(graph, fileIdx, AbsoluteFile toFullPath(graph.config, fileIdx)) + registerModule(graph, result) discard processModule(graph, result, if sfMainModule in flags and graph.config.projectIsStdin: stdin.llStreamOpen else: nil) elif graph.isDirty(result): @@ -128,6 +129,7 @@ proc compileProject*(graph: ModuleGraph; projectFileIdx = InvalidFileIDX) = proc makeModule*(graph: ModuleGraph; filename: AbsoluteFile): PSym = result = graph.newModule(fileInfoIdx(graph.config, filename)) result.id = getID() + registerModule(graph, result) proc makeModule*(graph: ModuleGraph; filename: string): PSym = result = makeModule(graph, AbsoluteFile filename) diff --git a/compiler/msgs.nim b/compiler/msgs.nim index dee1081f9..7e6b67cbe 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -190,10 +190,13 @@ template toFullPath*(conf: ConfigRef; info: TLineInfo): string = proc toMsgFilename*(conf: ConfigRef; info: TLineInfo): string = if info.fileIndex.int32 < 0: result = "???" - elif optListFullPaths in conf.globalOptions: - result = conf.m.fileInfos[info.fileIndex.int32].fullPath.string + return + let absPath = conf.m.fileInfos[info.fileIndex.int32].fullPath.string + let relPath = conf.m.fileInfos[info.fileIndex.int32].projPath.string + if optListFullPaths in conf.globalOptions: + result = absPath else: - result = conf.m.fileInfos[info.fileIndex.int32].projPath.string + result = if absPath.len < relPath.len: absPath else: relPath proc toLinenumber*(info: TLineInfo): int {.inline.} = result = int info.line diff --git a/compiler/nim.nim b/compiler/nim.nim index 5f3347255..1c4dbd3be 100644 --- a/compiler/nim.nim +++ b/compiler/nim.nim @@ -54,7 +54,8 @@ proc processCmdLine(pass: TCmdLinePass, cmd: string; config: ConfigRef) = of cmdArgument: if processArgument(pass, p, argsCount, config): break if pass == passCmd2: - if optRun notin config.globalOptions and config.arguments.len > 0 and config.command.normalize != "run": + if {optRun, optWasNimscript} * config.globalOptions == {} and + config.arguments.len > 0 and config.command.normalize notin ["run", "e"]: rawMessage(config, errGenerated, errArgsNeedRunOption) proc handleCmdLine(cache: IdentCache; conf: ConfigRef) = diff --git a/compiler/options.nim b/compiler/options.nim index 1280cb59b..80d665d62 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -45,7 +45,7 @@ type # please make sure we have under 32 options TOptions* = set[TOption] TGlobalOption* = enum # **keep binary compatible** gloptNone, optForceFullMake, - optDeadCodeElimUnused, # deprecated, always on + optWasNimscript, optListCmd, optCompileOnly, optNoLinking, optCDebug, # turn on debugging information optGenDynLib, # generate a dynamic library @@ -74,6 +74,7 @@ type # please make sure we have under 32 options optIdeTerse # idetools: use terse descriptions optNoCppExceptions # use C exception handling even with CPP optExcessiveStackTrace # fully qualified module filenames + optShowAllMismatches # show all overloading resolution candidates optWholeProject # for 'doc2': output any dependency optMixedMode # true if some module triggered C++ codegen optListFullPaths @@ -401,7 +402,8 @@ proc importantComments*(conf: ConfigRef): bool {.inline.} = conf.cmd in {cmdDoc, proc usesWriteBarrier*(conf: ConfigRef): bool {.inline.} = conf.selectedGC >= gcRefc template compilationCachePresent*(conf: ConfigRef): untyped = - conf.symbolFiles in {v2Sf, writeOnlySf} + false +# conf.symbolFiles in {v2Sf, writeOnlySf} template optPreserveOrigSource*(conf: ConfigRef): untyped = optEmbedOrigSrc in conf.globalOptions diff --git a/compiler/parser.nim b/compiler/parser.nim index 02083ca83..54b360a24 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -37,8 +37,7 @@ type TParser* = object # A TParser object represents a file that # is being parsed currInd: int # current indentation level - firstTok, strongSpaces: bool # Has the first token been read? - # Is strongSpaces on? + firstTok: bool # Has the first token been read? hasProgress: bool # some while loop requires progress ensurance lex*: TLexer # The lexer that is used for parsing tok*: TToken # The current token @@ -46,7 +45,7 @@ type inSemiStmtList*: int emptyNode: PNode when defined(nimpretty2): - em: Emitter + em*: Emitter SymbolMode = enum smNormal, smAllowNil, smAfterDot @@ -102,8 +101,7 @@ proc getTok(p: var TParser) = emitTok(p.em, p.lex, p.tok) proc openParser*(p: var TParser, fileIdx: FileIndex, inputStream: PLLStream, - cache: IdentCache; config: ConfigRef; - strongSpaces=false) = + cache: IdentCache; config: ConfigRef) = ## Open a parser, using the given arguments to set up its internal state. ## initToken(p.tok) @@ -112,13 +110,11 @@ proc openParser*(p: var TParser, fileIdx: FileIndex, inputStream: PLLStream, openEmitter(p.em, cache, config, fileIdx) getTok(p) # read the first token p.firstTok = true - p.strongSpaces = strongSpaces p.emptyNode = newNode(nkEmpty) proc openParser*(p: var TParser, filename: AbsoluteFile, inputStream: PLLStream, - cache: IdentCache; config: ConfigRef; - strongSpaces=false) = - openParser(p, fileInfoIdx(config, filename), inputStream, cache, config, strongSpaces) + cache: IdentCache; config: ConfigRef) = + openParser(p, fileInfoIdx(config, filename), inputStream, cache, config) proc closeParser(p: var TParser) = ## Close a parser, freeing up its resources. @@ -286,7 +282,7 @@ proc checkBinary(p: TParser) {.inline.} = #| #| prefixOperator = operator #| -#| optInd = COMMENT? +#| optInd = COMMENT? IND? #| optPar = (IND{>} | IND{=})? #| #| simpleExpr = arrowExpr (OP0 optInd arrowExpr)* pragma? @@ -706,8 +702,11 @@ proc namedParams(p: var TParser, callee: PNode, # progress guaranteed exprColonEqExprListAux(p, endTok, result) -proc commandParam(p: var TParser, isFirstParam: var bool): PNode = - result = parseExpr(p) +proc commandParam(p: var TParser, isFirstParam: var bool; mode: TPrimaryMode): PNode = + if mode == pmTypeDesc: + result = simpleExpr(p, mode) + else: + result = parseExpr(p) if p.tok.tokType == tkDo: result = postExprBlocks(p, result) elif p.tok.tokType == tkEquals and not isFirstParam: @@ -780,7 +779,7 @@ proc primarySuffix(p: var TParser, r: PNode, when true: # progress NOT guaranteed p.hasProgress = false - addSon result, commandParam(p, isFirstParam) + addSon result, commandParam(p, isFirstParam, mode) if not p.hasProgress: break else: while p.tok.tokType != tkEof: @@ -798,7 +797,7 @@ proc parseOperators(p: var TParser, headNode: PNode, limit: int, mode: TPrimaryMode): PNode = result = headNode # expand while operators have priorities higher than 'limit' - var opPrec = getPrecedence(p.tok, p.strongSpaces) + var opPrec = getPrecedence(p.tok, false) let modeB = if mode == pmTypeDef: pmTypeDesc else: mode # the operator itself must not start on a new line: # progress guaranteed @@ -815,7 +814,7 @@ proc parseOperators(p: var TParser, headNode: PNode, addSon(a, result) addSon(a, b) result = a - opPrec = getPrecedence(p.tok, p.strongSpaces) + opPrec = getPrecedence(p.tok, false) proc simpleExprAux(p: var TParser, limit: int, mode: TPrimaryMode): PNode = result = primary(p, mode) @@ -1255,14 +1254,29 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode = if mode != pmSkipSuffix: result = primarySuffix(p, result, baseInd, mode) +proc binaryNot(p: var TParser; a: PNode): PNode = + if p.tok.tokType == tkNot: + let notOpr = newIdentNodeP(p.tok.ident, p) + getTok(p) + optInd(p, notOpr) + let b = parseExpr(p) + result = newNodeP(nkInfix, p) + result.add notOpr + result.add a + result.add b + else: + result = a + proc parseTypeDesc(p: var TParser): PNode = - #| typeDesc = simpleExpr + #| typeDesc = simpleExpr ('not' expr)? result = simpleExpr(p, pmTypeDesc) + result = binaryNot(p, result) proc parseTypeDefAux(p: var TParser): PNode = - #| typeDefAux = simpleExpr + #| typeDefAux = simpleExpr ('not' expr)? #| | 'concept' typeClass result = simpleExpr(p, pmTypeDef) + result = binaryNot(p, result) proc makeCall(n: PNode): PNode = ## Creates a call if the given node isn't already a call. @@ -1370,12 +1384,12 @@ proc parseExprStmt(p: var TParser): PNode = while true: getTok(p) optInd(p, result) - addSon(result, commandParam(p, isFirstParam)) + addSon(result, commandParam(p, isFirstParam, pmNormal)) if p.tok.tokType != tkComma: break elif p.tok.indent < 0 and isExprStart(p): result = newNode(nkCommand, a.info, @[a]) while true: - addSon(result, commandParam(p, isFirstParam)) + addSon(result, commandParam(p, isFirstParam, pmNormal)) if p.tok.tokType != tkComma: break getTok(p) optInd(p, result) @@ -2236,10 +2250,8 @@ proc parseString*(s: string; cache: IdentCache; config: ConfigRef; stream.lineOffset = line var parser: TParser - # XXX for now the builtin 'parseStmt/Expr' functions do not know about strong - # spaces... parser.lex.errorHandler = errorHandler - openParser(parser, AbsoluteFile filename, stream, cache, config, false) + openParser(parser, AbsoluteFile filename, stream, cache, config) result = parser.parseAll closeParser(parser) diff --git a/compiler/pathutils.nim b/compiler/pathutils.nim index 4873f90d6..703467bc4 100644 --- a/compiler/pathutils.nim +++ b/compiler/pathutils.nim @@ -73,23 +73,6 @@ iterator dirs(x: string): (int, int) = var it: PathIter while hasNext(it, x): yield next(it, x) -when false: - iterator dirs(x: string): (int, int) = - var i = 0 - var first = true - while i < x.len: - let prev = i - if first and x[i] in {DirSep, AltSep}: - # absolute path: - inc i - else: - while i < x.len and x[i] notin {DirSep, AltSep}: inc i - if i > prev: - yield (prev, i-1) - first = false - # skip all separators: - while i < x.len and x[i] in {DirSep, AltSep}: inc i - proc isDot(x: string; bounds: (int, int)): bool = bounds[1] == bounds[0] and x[bounds[0]] == '.' diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 08e3c34d4..ef5223559 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -1019,8 +1019,10 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, else: incl(sym.typ.flags, tfIncompleteStruct) of wUnchecked: noVal(c, it) - if sym.typ == nil: invalidPragma(c, it) - else: incl(sym.typ.flags, tfUncheckedArray) + if sym.typ == nil or sym.typ.kind notin {tyArray, tyUncheckedArray}: + invalidPragma(c, it) + else: + sym.typ.kind = tyUncheckedArray of wUnion: noVal(c, it) if sym.typ == nil: invalidPragma(c, it) @@ -1060,6 +1062,8 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, invalidPragma(c, it) else: sym.bitsize = expectIntLit(c, it) + if sym.bitsize <= 0: + localError(c.config, it.info, "bitsize needs to be positive") of wGuard: if sym == nil or sym.kind notin {skVar, skLet, skField}: invalidPragma(c, it) diff --git a/compiler/reorder.nim b/compiler/reorder.nim index 07462bb3e..8c4d0d307 100644 --- a/compiler/reorder.nim +++ b/compiler/reorder.nim @@ -1,6 +1,6 @@ import - intsets, ast, idents, algorithm, renderer, parser, ospaths, strutils, + intsets, ast, idents, algorithm, renderer, parser, os, strutils, sequtils, msgs, modulegraphs, syntaxes, options, modulepaths, tables, lineinfos diff --git a/compiler/rod.nim b/compiler/rod.nim index f6fc24ec0..92489ffdd 100644 --- a/compiler/rod.nim +++ b/compiler/rod.nim @@ -22,6 +22,8 @@ when not nimIncremental: template storeRemaining*(g: ModuleGraph; module: PSym) = discard + template registerModule*(g: ModuleGraph; module: PSym) = discard + else: include rodimpl diff --git a/compiler/rodimpl.nim b/compiler/rodimpl.nim index 839a9c6ca..730328642 100644 --- a/compiler/rodimpl.nim +++ b/compiler/rodimpl.nim @@ -321,6 +321,9 @@ proc encodeSym(g: ModuleGraph, s: PSym, result: var string) = result.add('\16') encodeVInt(s.gcUnsafetyReason.id, result) pushSym(w, s.gcUnsafetyReason) + if s.transformedBody != nil: + result.add('\24') + encodeNode(g, s.info, s.transformedBody, result) of skModule, skPackage: encodeInstantiations(g, s.usedGenerics, result) # we don't serialize: @@ -363,13 +366,7 @@ proc storeType(g: ModuleGraph; t: PType) = db.exec(sql"insert into types(nimid, module, data) values (?, ?, ?)", t.id, mid, buf) -proc storeNode*(g: ModuleGraph; module: PSym; n: PNode) = - if g.config.symbolFiles == disabledSf: return - var buf = newStringOfCap(160) - encodeNode(g, module.info, n, buf) - db.exec(sql"insert into toplevelstmts(module, position, data) values (?, ?, ?)", - abs(module.id), module.offset, buf) - inc module.offset +proc transitiveClosure(g: ModuleGraph) = var i = 0 while true: if i > 10_000: @@ -388,9 +385,25 @@ proc storeNode*(g: ModuleGraph; module: PSym; n: PNode) = break inc i +proc storeNode*(g: ModuleGraph; module: PSym; n: PNode) = + if g.config.symbolFiles == disabledSf: return + var buf = newStringOfCap(160) + encodeNode(g, module.info, n, buf) + db.exec(sql"insert into toplevelstmts(module, position, data) values (?, ?, ?)", + abs(module.id), module.offset, buf) + inc module.offset + transitiveClosure(g) + proc recordStmt*(g: ModuleGraph; module: PSym; n: PNode) = storeNode(g, module, n) +proc storeFilename(g: ModuleGraph; fullpath: AbsoluteFile; fileIdx: FileIndex) = + let id = db.getValue(sql"select id from filenames where fullpath = ?", fullpath.string) + if id.len == 0: + let fullhash = hashFileCached(g.config, fileIdx, fullpath) + db.exec(sql"insert into filenames(nimid, fullpath, fullhash) values (?, ?, ?)", + int(fileIdx), fullpath.string, fullhash) + proc storeRemaining*(g: ModuleGraph; module: PSym) = if g.config.symbolFiles == disabledSf: return var stillForwarded: seq[PSym] = @[] @@ -400,6 +413,13 @@ proc storeRemaining*(g: ModuleGraph; module: PSym) = else: stillForwarded.add s swap w.forwardedSyms, stillForwarded + transitiveClosure(g) + var nimid = 0 + for x in items(g.config.m.fileInfos): + # don't store the "command line" entry: + if nimid != 0: + storeFilename(g, x.fullPath, FileIndex(nimid)) + inc nimid # ---------------- decoder ----------------------------------- @@ -725,6 +745,9 @@ proc loadSymFromBlob(g; b; info: TLineInfo): PSym = if b.s[b.pos] == '\16': inc(b.pos) result.gcUnsafetyReason = loadSym(g, decodeVInt(b.s, b.pos), result.info) + if b.s[b.pos] == '\24': + inc b.pos + result.transformedBody = decodeNode(g, b, result.info) of skModule, skPackage: decodeInstantiations(g, b, result.info, result.usedGenerics) of skLet, skVar, skField, skForVar: @@ -752,6 +775,9 @@ proc loadSym(g; id: int; info: TLineInfo): PSym = result = loadSymFromBlob(g, b, info) doAssert id == result.id, "symbol ID is not consistent!" +proc registerModule*(g; module: PSym) = + g.incr.r.syms.add(abs module.id, module) + proc loadModuleSymTab(g; module: PSym) = ## goal: fill module.tab g.incr.r.syms.add(module.id, module) @@ -765,7 +791,8 @@ proc loadModuleSymTab(g; module: PSym) = b.s.add '\0' s = loadSymFromBlob(g, b, module.info) assert s != nil - strTableAdd(module.tab, s) + if s.kind != skField: + strTableAdd(module.tab, s) if sfSystemModule in module.flags: g.systemModule = module @@ -843,8 +870,9 @@ proc replay(g: ModuleGraph; module: PSym; n: PNode) = internalAssert g.config, false of nkImportStmt: for x in n: - internalAssert g.config, x.kind == nkStrLit - let imported = g.importModuleCallback(g, module, fileInfoIdx(g.config, AbsoluteFile n[0].strVal)) + internalAssert g.config, x.kind == nkSym + let modpath = AbsoluteFile toFullPath(g.config, x.sym.info) + let imported = g.importModuleCallback(g, module, fileInfoIdx(g.config, modpath)) internalAssert g.config, imported.id < 0 of nkStmtList, nkStmtListExpr: for x in n: replay(g, module, x) @@ -868,16 +896,23 @@ proc setupModuleCache*(g: ModuleGraph) = let dbfile = getNimcacheDir(g.config) / RelativeFile"rodfiles.db" if g.config.symbolFiles == writeOnlySf: removeFile(dbfile) + createDir getNimcacheDir(g.config) + let ec = encodeConfig(g) if not fileExists(dbfile): db = open(connection=string dbfile, user="nim", password="", database="nim") createDb(db) - db.exec(sql"insert into config(config) values (?)", encodeConfig(g)) + db.exec(sql"insert into config(config) values (?)", ec) else: db = open(connection=string dbfile, user="nim", password="", database="nim") let oldConfig = db.getValue(sql"select config from config") - g.incr.configChanged = oldConfig != encodeConfig(g) + g.incr.configChanged = oldConfig != ec + # ensure the filename IDs stay consistent: + for row in db.rows(sql"select fullpath, nimid from filenames order by nimid"): + let id = fileInfoIdx(g.config, AbsoluteFile row[0]) + doAssert id.int == parseInt(row[1]) + db.exec(sql"update config set config = ?", ec) db.exec(sql"pragma journal_mode=off") # This MUST be turned off, otherwise it's way too slow even for testing purposes: db.exec(sql"pragma SYNCHRONOUS=off") diff --git a/compiler/scriptconfig.nim b/compiler/scriptconfig.nim index f94533177..bfff86479 100644 --- a/compiler/scriptconfig.nim +++ b/compiler/scriptconfig.nim @@ -123,6 +123,7 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string; cbconf setCommand: conf.command = a.getString 0 let arg = a.getString 1 + incl(conf.globalOptions, optWasNimscript) if arg.len > 0: conf.projectName = arg let path = @@ -158,6 +159,8 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string; proc runNimScript*(cache: IdentCache; scriptName: AbsoluteFile; freshDefines=true; conf: ConfigRef) = rawMessage(conf, hintConf, scriptName.string) + let oldSymbolFiles = conf.symbolFiles + conf.symbolFiles = disabledSf let graph = newModuleGraph(cache, conf) connectCallbacks(graph) @@ -183,3 +186,4 @@ proc runNimScript*(cache: IdentCache; scriptName: AbsoluteFile; #initDefines() undefSymbol(conf.symbols, "nimscript") undefSymbol(conf.symbols, "nimconfig") + conf.symbolFiles = oldSymbolFiles diff --git a/compiler/sem.nim b/compiler/sem.nim index 97a47ceca..775c9f7c9 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -16,13 +16,16 @@ import procfind, lookups, pragmas, passes, semdata, semtypinst, sigmatch, intsets, transf, vmdef, vm, idgen, aliases, cgmeth, lambdalifting, evaltempl, patterns, parampatterns, sempass2, linter, semmacrosanity, - semparallel, lowerings, pluginsupport, plugins/active, rod, lineinfos + lowerings, pluginsupport, plugins/active, rod, lineinfos -from modulegraphs import ModuleGraph, PPassContext +from modulegraphs import ModuleGraph, PPassContext, onUse, onDef, onDefResolveForward when defined(nimfix): import nimfix/prettybase +when not defined(leanCompiler): + import semparallel + # implementation proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode {.procvar.} @@ -447,7 +450,7 @@ proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym, pushInfoContext(c.config, nOrig.info, sym.detailedInfo) markUsed(c.config, n.info, sym, c.graph.usageSym) - styleCheckUse(n.info, sym) + onUse(n.info, sym) if sym == c.p.owner: globalError(c.config, n.info, "recursive dependency: '$1'" % sym.name.s) diff --git a/compiler/semasgn.nim b/compiler/semasgn.nim index 7d6ae70de..3947e4f6c 100644 --- a/compiler/semasgn.nim +++ b/compiler/semasgn.nim @@ -81,8 +81,8 @@ proc genAddr(c: PContext; x: PNode): PNode = addSon(result, x) proc newAsgnCall(c: PContext; op: PSym; x, y: PNode): PNode = - if sfError in op.flags: - localError(c.config, x.info, "usage of '$1' is a user-defined error" % op.name.s) + #if sfError in op.flags: + # localError(c.config, x.info, "usage of '$1' is a user-defined error" % op.name.s) result = newNodeI(nkCall, x.info) result.add newSymNode(op) result.add genAddr(c, x) @@ -121,8 +121,11 @@ proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode; op = field if op == nil: op = liftBody(c.c, t, c.kind, c.info) - markUsed(c.c.config, c.info, op, c.c.graph.usageSym) - styleCheckUse(c.info, op) + if sfError in op.flags: + incl c.fn.flags, sfError + else: + markUsed(c.c.config, c.info, op, c.c.graph.usageSym) + onUse(c.info, op) body.add newAsgnCall(c.c, op, x, y) result = true @@ -132,7 +135,7 @@ proc considerOverloadedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool = let op = t.destructor if op != nil: markUsed(c.c.config, c.info, op, c.c.graph.usageSym) - styleCheckUse(c.info, op) + onUse(c.info, op) body.add destructorCall(c.c, op, x) result = true of attachedAsgn: @@ -143,7 +146,7 @@ proc considerOverloadedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool = let op = t.deepCopy if op != nil: markUsed(c.c.config, c.info, op, c.c.graph.usageSym) - styleCheckUse(c.info, op) + onUse(c.info, op) body.add newDeepCopyCall(op, x, y) result = true @@ -200,7 +203,7 @@ proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) = tyPtr, tyRef, tyOpt, tyUncheckedArray: defaultOp(c, t, body, x, y) of tyArray: - if {tfHasAsgn, tfUncheckedArray} * t.flags == {tfHasAsgn}: + if tfHasAsgn in t.flags: let i = declareCounter(c, body, firstOrd(c.c.config, t)) let whileLoop = genWhileLoop(c, i, x) let elemType = t.lastSon diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 49b344274..7e0ea5490 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -138,6 +138,7 @@ proc effectProblem(f, a: PType; result: var string) = proc renderNotLValue(n: PNode): string = result = $n + let n = if n.kind == nkHiddenDeref: n[0] else: n if n.kind == nkHiddenCallConv and n.len > 1: result = $n[0] & "(" & result & ")" elif n.kind in {nkHiddenStdConv, nkHiddenSubConv} and n.len == 2: @@ -166,20 +167,22 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors): prefer = preferModuleInfo break - when false: - # we pretend procs are attached to the type of the first - # argument in order to remove plenty of candidates. This is - # comparable to what C# does and C# is doing fine. - var filterOnlyFirst = false + # we pretend procs are attached to the type of the first + # argument in order to remove plenty of candidates. This is + # comparable to what C# does and C# is doing fine. + var filterOnlyFirst = false + if optShowAllMismatches notin c.config.globalOptions: for err in errors: if err.firstMismatch > 1: filterOnlyFirst = true break var candidates = "" + var skipped = 0 for err in errors: - when false: - if filterOnlyFirst and err.firstMismatch == 1: continue + if filterOnlyFirst and err.firstMismatch == 1: + inc skipped + continue if err.sym.kind in routineKinds and err.sym.ast != nil: add(candidates, renderTree(err.sym.ast, {renderNoBody, renderNoComments, renderNoPragmas})) @@ -216,7 +219,9 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors): "' is immutable\n") for diag in err.diagnostics: candidates.add(diag & "\n") - + if skipped > 0: + candidates.add($skipped & " other mismatching symbols have been " & + " suppressed; compile with --showAllMismatches:on to see them\n") result = (prefer, candidates) const @@ -390,6 +395,7 @@ proc resolveOverloads(c: PContext, n, orig: PNode, args]) proc instGenericConvertersArg*(c: PContext, a: PNode, x: TCandidate) = + let a = if a.kind == nkHiddenDeref: a[0] else: a if a.kind == nkHiddenCallConv and a.sons[0].kind == nkSym: let s = a.sons[0].sym if s.ast != nil and s.ast[genericParamsPos].kind != nkEmpty: @@ -450,7 +456,7 @@ proc semResolvedCall(c: PContext, x: TCandidate, assert x.state == csMatch var finalCallee = x.calleeSym markUsed(c.config, n.sons[0].info, finalCallee, c.graph.usageSym) - styleCheckUse(n.sons[0].info, finalCallee) + onUse(n.sons[0].info, finalCallee) assert finalCallee.ast != nil if x.hasFauxMatch: result = x.call @@ -558,14 +564,17 @@ proc explicitGenericSym(c: PContext, n: PNode, s: PSym): PNode = var newInst = generateInstance(c, s, m.bindings, n.info) newInst.typ.flags.excl tfUnresolved markUsed(c.config, n.info, s, c.graph.usageSym) - styleCheckUse(n.info, s) + onUse(n.info, s) result = newSymNode(newInst, n.info) proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode = assert n.kind == nkBracketExpr for i in 1..sonsLen(n)-1: let e = semExpr(c, n.sons[i]) - n.sons[i].typ = e.typ.skipTypes({tyTypeDesc}) + if e.typ == nil: + localError(c.config, e.info, "expression has no type") + else: + n.sons[i].typ = e.typ.skipTypes({tyTypeDesc}) var s = s var a = n.sons[0] if a.kind == nkSym: diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 1725d7a31..669862c56 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -25,7 +25,7 @@ const proc semTemplateExpr(c: PContext, n: PNode, s: PSym, flags: TExprFlags = {}): PNode = markUsed(c.config, n.info, s, c.graph.usageSym) - styleCheckUse(n.info, s) + onUse(n.info, s) pushInfoContext(c.config, n.info, s.detailedInfo) result = evalTemplate(n, s, getCurrOwner(c), c.config, efFromHlo in flags) if efNoSemCheck notin flags: result = semAfterMacroCall(c, n, result, s, flags) @@ -265,7 +265,7 @@ proc semConv(c: PContext, n: PNode): PNode = let status = checkConvertible(c, result.typ, it.typ) if status in {convOK, convNotNeedeed}: markUsed(c.config, n.info, it.sym, c.graph.usageSym) - styleCheckUse(n.info, it.sym) + onUse(n.info, it.sym) markIndirect(c, it.sym) return it errorUseQualifier(c, n.info, op.sons[0].sym) @@ -614,7 +614,8 @@ proc analyseIfAddressTakenInCall(c: PContext, n: PNode) = const FakeVarParams = {mNew, mNewFinalize, mInc, ast.mDec, mIncl, mExcl, mSetLengthStr, mSetLengthSeq, mAppendStrCh, mAppendStrStr, mSwap, - mAppendSeqElem, mNewSeq, mReset, mShallowCopy, mDeepCopy} + mAppendSeqElem, mNewSeq, mReset, mShallowCopy, mDeepCopy, mMove, + mWasMoved} # get the real type of the callee # it may be a proc var with a generic alias type, so we skip over them @@ -640,6 +641,7 @@ proc analyseIfAddressTakenInCall(c: PContext, n: PNode) = return for i in countup(1, sonsLen(n) - 1): + let n = if n.kind == nkHiddenDeref: n[0] else: n if n.sons[i].kind == nkHiddenCallConv: # we need to recurse explicitly here as converters can create nested # calls and then they wouldn't be analysed otherwise @@ -1036,7 +1038,7 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode = case s.kind of skConst: markUsed(c.config, n.info, s, c.graph.usageSym) - styleCheckUse(n.info, s) + onUse(n.info, s) case skipTypes(s.typ, abstractInst-{tyTypeDesc}).kind of tyNil, tyChar, tyInt..tyInt64, tyFloat..tyFloat128, tyTuple, tySet, tyUInt..tyUInt64: @@ -1061,7 +1063,7 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode = if efNoEvaluateGeneric in flags and s.ast[genericParamsPos].len > 0 or (n.kind notin nkCallKinds and s.requiredParams > 0): markUsed(c.config, n.info, s, c.graph.usageSym) - styleCheckUse(n.info, s) + onUse(n.info, s) result = symChoice(c, n, s, scClosed) else: result = semMacroExpr(c, n, n, s, flags) @@ -1070,13 +1072,13 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode = (n.kind notin nkCallKinds and s.requiredParams > 0) or sfCustomPragma in sym.flags: markUsed(c.config, n.info, s, c.graph.usageSym) - styleCheckUse(n.info, s) + onUse(n.info, s) result = symChoice(c, n, s, scClosed) else: result = semTemplateExpr(c, n, s, flags) of skParam: markUsed(c.config, n.info, s, c.graph.usageSym) - styleCheckUse(n.info, s) + onUse(n.info, s) if s.typ != nil and s.typ.kind == tyStatic and s.typ.n != nil: # XXX see the hack in sigmatch.nim ... return s.typ.n @@ -1098,14 +1100,14 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode = localError(c.config, n.info, "illegal context for 'nimvm' magic") markUsed(c.config, n.info, s, c.graph.usageSym) - styleCheckUse(n.info, s) + onUse(n.info, s) result = newSymNode(s, n.info) # We cannot check for access to outer vars for example because it's still # not sure the symbol really ends up being used: # var len = 0 # but won't be called # genericThatUsesLen(x) # marked as taking a closure? of skGenericParam: - styleCheckUse(n.info, s) + onUse(n.info, s) if s.typ.kind == tyStatic: result = newSymNode(s, n.info) result.typ = s.typ @@ -1116,7 +1118,7 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode = return n of skType: markUsed(c.config, n.info, s, c.graph.usageSym) - styleCheckUse(n.info, s) + onUse(n.info, s) if s.typ.kind == tyStatic and s.typ.base.kind != tyNone and s.typ.n != nil: return s.typ.n result = newSymNode(s, n.info) @@ -1138,7 +1140,7 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode = # is the access to a public field or in the same module or in a friend? doAssert f == s markUsed(c.config, n.info, f, c.graph.usageSym) - styleCheckUse(n.info, f) + onUse(n.info, f) result = newNodeIT(nkDotExpr, n.info, f.typ) result.add makeDeref(newSymNode(p.selfSym)) result.add newSymNode(f) # we now have the correct field @@ -1151,11 +1153,11 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode = ty = skipTypes(ty.sons[0], skipPtrs) # old code, not sure if it's live code: markUsed(c.config, n.info, s, c.graph.usageSym) - styleCheckUse(n.info, s) + onUse(n.info, s) result = newSymNode(s, n.info) else: markUsed(c.config, n.info, s, c.graph.usageSym) - styleCheckUse(n.info, s) + onUse(n.info, s) result = newSymNode(s, n.info) proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = @@ -1178,7 +1180,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = else: markUsed(c.config, n.sons[1].info, s, c.graph.usageSym) result = semSym(c, n, s, flags) - styleCheckUse(n.sons[1].info, s) + onUse(n.sons[1].info, s) return n.sons[0] = semExprWithType(c, n.sons[0], flags+{efDetermineType}) @@ -1241,7 +1243,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = result.info = n.info result.typ = ty markUsed(c.config, n.info, f, c.graph.usageSym) - styleCheckUse(n.info, f) + onUse(n.info, f) return of tyObject, tyTuple: if ty.n != nil and ty.n.kind == nkRecList: @@ -1272,7 +1274,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = if fieldVisible(c, f): # is the access to a public field or in the same module or in a friend? markUsed(c.config, n.sons[1].info, f, c.graph.usageSym) - styleCheckUse(n.sons[1].info, f) + onUse(n.sons[1].info, f) n.sons[0] = makeDeref(n.sons[0]) n.sons[1] = newSymNode(f) # we now have the correct field n.typ = f.typ @@ -1286,7 +1288,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = f = getSymFromList(ty.n, i) if f != nil: markUsed(c.config, n.sons[1].info, f, c.graph.usageSym) - styleCheckUse(n.sons[1].info, f) + onUse(n.sons[1].info, f) n.sons[0] = makeDeref(n.sons[0]) n.sons[1] = newSymNode(f) n.typ = f.typ @@ -1538,7 +1540,9 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode = # a = b # both are vars, means: a[] = b[] # a = b # b no 'var T' means: a = addr(b) var le = a.typ - if (skipTypes(le, {tyGenericInst, tyAlias, tySink}).kind != tyVar and + if le == nil: + localError(c.config, a.info, "expression has no type") + elif (skipTypes(le, {tyGenericInst, tyAlias, tySink}).kind != tyVar and isAssignable(c, a) == arNone) or skipTypes(le, abstractVar).kind in {tyOpenArray, tyVarargs}: # Direct assignment to a discriminant is allowed! @@ -1756,7 +1760,7 @@ proc semExpandToAst(c: PContext, n: PNode): PNode = macroCall.sons[0] = newSymNode(expandedSym, macroCall.info) markUsed(c.config, n.info, expandedSym, c.graph.usageSym) - styleCheckUse(n.info, expandedSym) + onUse(n.info, expandedSym) if isCallExpr(macroCall): for i in countup(1, macroCall.len-1): @@ -1781,7 +1785,7 @@ proc semExpandToAst(c: PContext, n: PNode): PNode = let info = macroCall.sons[0].info macroCall.sons[0] = newSymNode(cand, info) markUsed(c.config, info, cand, c.graph.usageSym) - styleCheckUse(info, cand) + onUse(info, cand) # we just perform overloading resolution here: #n.sons[1] = semOverloadedCall(c, macroCall, macroCall, {skTemplate, skMacro}) @@ -1811,6 +1815,7 @@ proc processQuotations(c: PContext; n: var PNode, op: string, ids.add n return + if n.kind == nkPrefix: checkSonsLen(n, 2, c.config) if n[0].kind == nkIdent: @@ -1821,6 +1826,9 @@ proc processQuotations(c: PContext; n: var PNode, op: string, n.sons[0] = newIdentNode(getIdent(c.cache, examinedOp.substr(op.len)), n.info) elif n.kind == nkAccQuoted and op == "``": returnQuote n[0] + elif n.kind == nkIdent: + if n.ident.s == "result": + n = ids[0] for i in 0 ..< n.safeLen: processQuotations(c, n.sons[i], op, quotes, ids) @@ -1832,15 +1840,18 @@ proc semQuoteAst(c: PContext, n: PNode): PNode = var quotedBlock = n[^1] op = if n.len == 3: expectString(c, n[1]) else: "``" - quotes = newSeq[PNode](1) + quotes = newSeq[PNode](2) # the quotes will be added to a nkCall statement - # leave some room for the callee symbol - ids = newSeq[PNode]() + # leave some room for the callee symbol and the result symbol + ids = newSeq[PNode](1) # this will store the generated param names + # leave some room for the result symbol if quotedBlock.kind != nkStmtList: localError(c.config, n.info, errXExpected, "block") + # This adds a default first field to pass the result symbol + ids[0] = newAnonSym(c, skParam, n.info).newSymNode processQuotations(c, quotedBlock, op, quotes, ids) var dummyTemplate = newProcNode( @@ -1859,6 +1870,8 @@ proc semQuoteAst(c: PContext, n: PNode): PNode = var tmpl = semTemplateDef(c, dummyTemplate) quotes[0] = tmpl[namePos] + # This adds a call to newIdentNode("result") as the first argument to the template call + quotes[1] = newNode(nkCall, n.info, @[newIdentNode(getIdent(c.cache, "newIdentNode"), n.info), newStrNode(nkStrLit, "result")]) result = newNode(nkCall, n.info, @[ createMagic(c.graph, "getAst", mExpandToAst).newSymNode, newNode(nkCall, n.info, quotes)]) @@ -2254,6 +2267,7 @@ proc semBlock(c: PContext, n: PNode; flags: TExprFlags): PNode = n.sons[0] = newSymNode(labl, n.sons[0].info) suggestSym(c.config, n.sons[0].info, labl, c.graph.usageSym) styleCheckDef(c.config, labl) + onDef(n[0].info, labl) n.sons[1] = semExpr(c, n.sons[1], flags) n.typ = n.sons[1].typ if isEmptyType(n.typ): n.kind = nkBlockStmt @@ -2332,7 +2346,6 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = {checkUndeclared, checkModule, checkAmbiguity, checkPureEnumFields} var s = qualifiedLookUp(c, n, checks) if c.matchedConcept == nil: semCaptureSym(s, c.p.owner) - result = semSym(c, n, s, flags) if s.kind in {skProc, skFunc, skMethod, skConverter, skIterator}: #performProcvarCheck(c, n, s) result = symChoice(c, n, s, scClosed) @@ -2340,6 +2353,8 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = markIndirect(c, result.sym) # if isGenericRoutine(result.sym): # localError(c.config, n.info, errInstantiateXExplicitly, s.name.s) + else: + result = semSym(c, n, s, flags) of nkSym: # because of the changed symbol binding, this does not mean that we # don't have to check the symbol for semantics here again! diff --git a/compiler/semfold.nim b/compiler/semfold.nim index 5565e8ed9..5ec702257 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -267,12 +267,7 @@ proc evalOp(m: TMagic, n, a, b, c: PNode; g: ModuleGraph): PNode = of mSubF64: result = newFloatNodeT(getFloat(a) - getFloat(b), n, g) of mMulF64: result = newFloatNodeT(getFloat(a) * getFloat(b), n, g) of mDivF64: - if getFloat(b) == 0.0: - if getFloat(a) == 0.0: result = newFloatNodeT(NaN, n, g) - elif getFloat(b).classify == fcNegZero: result = newFloatNodeT(-Inf, n, g) - else: result = newFloatNodeT(Inf, n, g) - else: - result = newFloatNodeT(getFloat(a) / getFloat(b), n, g) + result = newFloatNodeT(getFloat(a) / getFloat(b), n, g) of mMaxF64: if getFloat(a) > getFloat(b): result = newFloatNodeT(getFloat(a), n, g) else: result = newFloatNodeT(getFloat(b), n, g) @@ -413,15 +408,14 @@ proc getAppType(n: PNode; g: ModuleGraph): PNode = result = newStrNodeT("console", n, g) proc rangeCheck(n: PNode, value: BiggestInt; g: ModuleGraph) = - if tfUncheckedArray notin n.typ.flags: - var err = false - if n.typ.skipTypes({tyRange}).kind in {tyUInt..tyUInt64}: - err = value <% firstOrd(g.config, n.typ) or value >% lastOrd(g.config, n.typ, fixedUnsigned=true) - else: - err = value < firstOrd(g.config, n.typ) or value > lastOrd(g.config, n.typ) - if err: - localError(g.config, n.info, "cannot convert " & $value & - " to " & typeToString(n.typ)) + var err = false + if n.typ.skipTypes({tyRange}).kind in {tyUInt..tyUInt64}: + err = value <% firstOrd(g.config, n.typ) or value >% lastOrd(g.config, n.typ, fixedUnsigned=true) + else: + err = value < firstOrd(g.config, n.typ) or value > lastOrd(g.config, n.typ) + if err: + localError(g.config, n.info, "cannot convert " & $value & + " to " & typeToString(n.typ)) proc foldConv(n, a: PNode; g: ModuleGraph; check = false): PNode = let dstTyp = skipTypes(n.typ, abstractRange) diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim index a0044a0af..5972f3b55 100644 --- a/compiler/semgnrc.nim +++ b/compiler/semgnrc.nim @@ -76,14 +76,14 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym, result = symChoice(c, n, s, scOpen) of skTemplate: if macroToExpandSym(s): - styleCheckUse(n.info, s) + onUse(n.info, s) result = semTemplateExpr(c, n, s, {efNoSemCheck}) result = semGenericStmt(c, result, {}, ctx) else: result = symChoice(c, n, s, scOpen) of skMacro: if macroToExpandSym(s): - styleCheckUse(n.info, s) + onUse(n.info, s) result = semMacroExpr(c, n, n, s, {efNoSemCheck}) result = semGenericStmt(c, result, {}, ctx) else: @@ -96,20 +96,20 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym, result = n else: result = newSymNodeTypeDesc(s, n.info) - styleCheckUse(n.info, s) + onUse(n.info, s) of skParam: result = n - styleCheckUse(n.info, s) + onUse(n.info, s) of skType: if (s.typ != nil) and (s.typ.flags * {tfGenericTypeParam, tfImplicitTypeParam} == {}): result = newSymNodeTypeDesc(s, n.info) else: result = n - styleCheckUse(n.info, s) + onUse(n.info, s) else: result = newSymNode(s, n.info) - styleCheckUse(n.info, s) + onUse(n.info, s) proc lookup(c: PContext, n: PNode, flags: TSemGenericFlags, ctx: var GenericCtx): PNode = @@ -172,6 +172,7 @@ proc addTempDecl(c: PContext; n: PNode; kind: TSymKind) = let s = newSymS(skUnknown, getIdentNode(c, n), c) addPrelimDecl(c, s) styleCheckDef(c.config, n.info, s, kind) + onDef(n.info, s) proc semGenericStmt(c: PContext, n: PNode, flags: TSemGenericFlags, ctx: var GenericCtx): PNode = @@ -230,7 +231,7 @@ proc semGenericStmt(c: PContext, n: PNode, case s.kind of skMacro: if macroToExpand(s) and sc.safeLen <= 1: - styleCheckUse(fn.info, s) + onUse(fn.info, s) result = semMacroExpr(c, n, n, s, {efNoSemCheck}) result = semGenericStmt(c, result, flags, ctx) else: @@ -239,7 +240,7 @@ proc semGenericStmt(c: PContext, n: PNode, mixinContext = true of skTemplate: if macroToExpand(s) and sc.safeLen <= 1: - styleCheckUse(fn.info, s) + onUse(fn.info, s) result = semTemplateExpr(c, n, s, {efNoSemCheck}) result = semGenericStmt(c, result, flags, ctx) else: @@ -262,17 +263,17 @@ proc semGenericStmt(c: PContext, n: PNode, inc first of skGenericParam: result.sons[0] = newSymNodeTypeDesc(s, fn.info) - styleCheckUse(fn.info, s) + onUse(fn.info, s) first = 1 of skType: # bad hack for generics: if (s.typ != nil) and (s.typ.kind != tyGenericParam): result.sons[0] = newSymNodeTypeDesc(s, fn.info) - styleCheckUse(fn.info, s) + onUse(fn.info, s) first = 1 else: result.sons[0] = newSymNode(s, fn.info) - styleCheckUse(fn.info, s) + onUse(fn.info, s) first = 1 elif fn.kind == nkDotExpr: result.sons[0] = fuzzyLookup(c, fn, flags, ctx, mixinContext) diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index 09d971236..2aae562f9 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -306,7 +306,13 @@ proc semOf(c: PContext, n: PNode): PNode = result.typ = getSysType(c.graph, n.info, tyBool) return result elif diff == high(int): - localError(c.config, n.info, "'$1' cannot be of this subtype" % typeToString(a)) + if commonSuperclass(a, b) == nil: + localError(c.config, n.info, "'$1' cannot be of this subtype" % typeToString(a)) + else: + message(c.config, n.info, hintConditionAlwaysFalse, renderTree(n)) + result = newIntNode(nkIntLit, 0) + result.info = n.info + result.typ = getSysType(c.graph, n.info, tyBool) else: localError(c.config, n.info, "'of' takes 2 arguments") n.typ = getSysType(c.graph, n.info, tyBool) @@ -329,23 +335,19 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode, of mTypeOf: result = semTypeOf(c, n) of mSizeOf: - # TODO there is no proper way to find out if a type cannot be queried for the size. - let size = getSize(c.config, n[1].typ) - # We just assume here that the type might come from the c backend - if size == szUnknownSize: - # Forward to the c code generation to emit a `sizeof` in the C code. - result = n - elif size >= 0: - result = newIntNode(nkIntLit, size) - result.info = n.info - result.typ = n.typ - else: - - localError(c.config, n.info, "cannot evaluate 'sizeof' because its type is not defined completely") - - result = nil - - + # TODO there is no proper way to find out if a type cannot be queried for the size. + let size = getSize(c.config, n[1].typ) + # We just assume here that the type might come from the c backend + if size == szUnknownSize: + # Forward to the c code generation to emit a `sizeof` in the C code. + result = n + elif size >= 0: + result = newIntNode(nkIntLit, size) + result.info = n.info + result.typ = n.typ + else: + localError(c.config, n.info, "cannot evaluate 'sizeof' because its type is not defined completely") + result = n of mAlignOf: result = newIntNode(nkIntLit, getAlign(c.config, n[1].typ)) result.info = n.info diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index c1bdb08a8..0317fd8ba 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -9,9 +9,12 @@ import intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees, - wordrecg, strutils, options, guards, writetracking, lineinfos, semfold, + wordrecg, strutils, options, guards, lineinfos, semfold, modulegraphs +when not defined(leanCompiler): + import writetracking + when defined(useDfa): import dfa @@ -713,7 +716,7 @@ proc track(tracked: PEffects, n: PNode) = track(tracked, n.sons[i]) of nkCallKinds: if getConstExpr(tracked.owner_module, n, tracked.graph) != nil: - return + return # p's effects are ours too: var a = n.sons[0] let op = a.typ diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 5c7f866ce..1f2b9f0b3 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -41,8 +41,13 @@ proc semDiscard(c: PContext, n: PNode): PNode = checkSonsLen(n, 1, c.config) if n.sons[0].kind != nkEmpty: n.sons[0] = semExprWithType(c, n.sons[0]) - if isEmptyType(n.sons[0].typ) or n.sons[0].typ.kind == tyNone or n.sons[0].kind == nkTypeOfExpr: + let sonType = n.sons[0].typ + let sonKind = n.sons[0].kind + if isEmptyType(sonType) or sonType.kind == tyNone or n.sons[0].kind == nkTypeOfExpr: localError(c.config, n.info, errInvalidDiscard) + if sonType.kind == tyProc and sonKind notin nkCallKinds: + # tyProc is disallowed to prevent ``discard foo`` to be valid, when ``discard foo()`` is meant. + localError(c.config, n.info, "illegal discard proc, did you mean: " & $n[0] & "()") proc semBreakOrContinue(c: PContext, n: PNode): PNode = result = n @@ -61,7 +66,7 @@ proc semBreakOrContinue(c: PContext, n: PNode): PNode = incl(s.flags, sfUsed) n.sons[0] = x suggestSym(c.config, x.info, s, c.graph.usageSym) - styleCheckUse(x.info, s) + onUse(x.info, s) else: localError(c.config, n.info, errInvalidControlFlowX % s.name.s) else: @@ -359,6 +364,7 @@ proc semUsing(c: PContext; n: PNode): PNode = for j in countup(0, length-3): let v = semIdentDef(c, a.sons[j], skParam) styleCheckDef(c.config, v) + onDef(a[j].info, v) v.typ = typ strTableIncl(c.signatures, v) else: @@ -490,6 +496,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = continue var v = semIdentDef(c, a.sons[j], symkind) styleCheckDef(c.config, v) + onDef(a[j].info, v) if sfGenSym notin v.flags and not isDiscardUnderscore(v): addInterfaceDecl(c, v) when oKeepVariableNames: @@ -544,6 +551,7 @@ proc semConst(c: PContext, n: PNode): PNode = checkSonsLen(a, 3, c.config) var v = semIdentDef(c, a.sons[0], skConst) styleCheckDef(c.config, v) + onDef(a[0].info, v) var typ: PType = nil if a.sons[1].kind != nkEmpty: typ = semTypeNode(c, a.sons[1], nil) @@ -587,6 +595,7 @@ proc symForVar(c: PContext, n: PNode): PSym = let m = if n.kind == nkPragmaExpr: n.sons[0] else: n result = newSymG(skForVar, m, c) styleCheckDef(c.config, result) + onDef(n.info, result) if n.kind == nkPragmaExpr: pragma(c, result, n.sons[1], forVarPragmas) @@ -692,7 +701,7 @@ proc handleCaseStmtMacro(c: PContext; n: PNode): PNode = if r.state == csMatch: var match = r.calleeSym markUsed(c.config, n[0].info, match, c.graph.usageSym) - styleCheckUse(n[0].info, match) + onUse(n[0].info, match) # but pass 'n' to the 'match' macro, not 'n[0]': r.call.sons[1] = n @@ -876,6 +885,7 @@ proc typeSectionLeftSidePass(c: PContext, n: PNode) = if typsym.isNil: s = semIdentDef(c, name[1], skType) styleCheckDef(c.config, s) + onDef(name[1].info, s) s.typ = newTypeS(tyObject, c) s.typ.sym = s s.flags.incl sfForward @@ -890,6 +900,7 @@ proc typeSectionLeftSidePass(c: PContext, n: PNode) = else: s = semIdentDef(c, name, skType) styleCheckDef(c.config, s) + onDef(name.info, s) s.typ = newTypeS(tyForward, c) s.typ.sym = s # process pragmas: if name.kind == nkPragmaExpr: @@ -1621,6 +1632,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, else: implicitPragmas(c, s, n, validPragmas) styleCheckDef(c.config, s) + onDef(n[namePos].info, s) else: if n.sons[pragmasPos].kind != nkEmpty: pragma(c, s, n.sons[pragmasPos], validPragmas) @@ -1634,6 +1646,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, localError(c.config, n.sons[pragmasPos].info, errPragmaOnlyInHeaderOfProcX % ("'" & proto.name.s & "' from " & c.config$proto.info)) styleCheckDef(c.config, s) + onDefResolveForward(n[namePos].info, proto) if sfForward notin proto.flags and proto.magic == mNone: wrongRedefinition(c, n.info, proto.name.s, proto.info) excl(proto.flags, sfForward) @@ -1666,7 +1679,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, localError(c.config, n.info, "the overloaded " & s.name.s & " operator has to be enabled with {.experimental: \"callOperator\".}") - if n.sons[bodyPos].kind != nkEmpty: + if n.sons[bodyPos].kind != nkEmpty and sfError notin s.flags: # for DLL generation it is annoying to check for sfImportc! if sfBorrow in s.flags: localError(c.config, n.sons[bodyPos].info, errImplOfXNotAllowed % s.name.s) diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index a64315037..14507cf9d 100644 --- a/compiler/semtempl.nim +++ b/compiler/semtempl.nim @@ -64,6 +64,7 @@ proc symChoice(c: PContext, n: PNode, s: PSym, r: TSymChoiceRule): PNode = # for instance 'nextTry' is both in tables.nim and astalgo.nim ... result = newSymNode(s, n.info) markUsed(c.config, n.info, s, c.graph.usageSym) + onUse(n.info, s) else: # semantic checking requires a type; ``fitNode`` deals with it # appropriately @@ -75,6 +76,7 @@ proc symChoice(c: PContext, n: PNode, s: PSym, r: TSymChoiceRule): PNode = if a.kind != skModule: incl(a.flags, sfUsed) addSon(result, newSymNode(a, n.info)) + onUse(n.info, a) a = nextOverloadIter(o, c, n) proc semBindStmt(c: PContext, n: PNode, toBind: var IntSet): PNode = @@ -160,7 +162,7 @@ proc onlyReplaceParams(c: var TemplCtx, n: PNode): PNode = if s.owner == c.owner and s.kind == skParam: incl(s.flags, sfUsed) result = newSymNode(s, n.info) - styleCheckUse(n.info, s) + onUse(n.info, s) else: for i in 0 ..< n.safeLen: result.sons[i] = onlyReplaceParams(c, n.sons[i]) @@ -208,19 +210,20 @@ proc addLocalDecl(c: var TemplCtx, n: var PNode, k: TSymKind) = # So we need only check the *current* scope. let s = localSearchInScope(c.c, considerQuotedIdent(c.c, ident)) if s != nil and s.owner == c.owner and sfGenSym in s.flags: - styleCheckUse(n.info, s) + onUse(n.info, s) replaceIdentBySym(c.c, n, newSymNode(s, n.info)) elif not (n.kind == nkSym and sfGenSym in n.sym.flags): let local = newGenSym(k, ident, c) addPrelimDecl(c.c, local) styleCheckDef(c.c.config, n.info, local) + onDef(n.info, local) replaceIdentBySym(c.c, n, newSymNode(local, n.info)) else: replaceIdentBySym(c.c, n, ident) proc semTemplSymbol(c: PContext, n: PNode, s: PSym): PNode = incl(s.flags, sfUsed) - # we do not call styleCheckUse here, as the identifier is not really + # we do not call onUse here, as the identifier is not really # resolved here. We will fixup the used identifiers later. case s.kind of skUnknown: @@ -245,7 +248,7 @@ proc semRoutineInTemplName(c: var TemplCtx, n: PNode): PNode = if s.owner == c.owner and (s.kind == skParam or sfGenSym in s.flags): incl(s.flags, sfUsed) result = newSymNode(s, n.info) - styleCheckUse(n.info, s) + onUse(n.info, s) else: for i in countup(0, safeLen(n) - 1): result.sons[i] = semRoutineInTemplName(c, n.sons[i]) @@ -261,6 +264,7 @@ proc semRoutineInTemplBody(c: var TemplCtx, n: PNode, k: TSymKind): PNode = s.ast = n addPrelimDecl(c.c, s) styleCheckDef(c.c.config, n.info, s) + onDef(n.info, s) n.sons[namePos] = newSymNode(s, n.sons[namePos].info) else: n.sons[namePos] = ident @@ -314,7 +318,7 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode = if s.owner == c.owner and s.kind == skParam: incl(s.flags, sfUsed) result = newSymNode(s, n.info) - styleCheckUse(n.info, s) + onUse(n.info, s) elif contains(c.toBind, s.id): result = symChoice(c.c, n, s, scClosed) elif contains(c.toMixin, s.name.id): @@ -324,7 +328,7 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode = # var yz: T incl(s.flags, sfUsed) result = newSymNode(s, n.info) - styleCheckUse(n.info, s) + onUse(n.info, s) else: result = semTemplSymbol(c.c, n, s) of nkBind: @@ -382,6 +386,7 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode = let s = newGenSym(skLabel, n.sons[0], c) addPrelimDecl(c.c, s) styleCheckDef(c.c.config, s) + onDef(n[0].info, s) n.sons[0] = newSymNode(s, n.sons[0].info) n.sons[1] = semTemplBody(c, n.sons[1]) closeScope(c) @@ -505,7 +510,7 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode = if s.owner == c.owner and s.kind == skParam and n.kind == nkAccQuoted and n.len == 1: incl(s.flags, sfUsed) - styleCheckUse(n.info, s) + onUse(n.info, s) return newSymNode(s, n.info) elif contains(c.toBind, s.id): return symChoice(c.c, n, s, scClosed) @@ -553,6 +558,7 @@ proc semTemplateDef(c: PContext, n: PNode): PNode = else: s = semIdentVis(c, skTemplate, n.sons[0], {}) styleCheckDef(c.config, s) + onDef(n[0].info, s) # check parameter list: #s.scope = c.currentScope pushOwner(c, s) @@ -635,7 +641,7 @@ proc semPatternBody(c: var TemplCtx, n: PNode): PNode = # semtypes.addParamOrResult). Within the pattern we have to ensure # to use the param with the proper type though: incl(s.flags, sfUsed) - styleCheckUse(n.info, s) + onUse(n.info, s) let x = c.owner.typ.n.sons[s.position+1].sym assert x.name == s.name result = newSymNode(x, n.info) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 5215a1d11..f4a1b0302 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -122,6 +122,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType = if not isPure: strTableAdd(c.module.tab, e) addSon(result.n, newSymNode(e)) styleCheckDef(c.config, e) + onDef(e.info, e) if sfGenSym notin e.flags: if not isPure: addDecl(c, e) else: importPureEnumField(c, e) @@ -377,7 +378,7 @@ proc semTypeIdent(c: PContext, n: PNode): PSym = result = qualifiedLookUp(c, n, {checkAmbiguity, checkUndeclared}) if result != nil: markUsed(c.config, n.info, result, c.graph.usageSym) - styleCheckUse(n.info, result) + onUse(n.info, result) if result.kind == skParam and result.typ.kind == tyTypeDesc: # This is a typedesc param. is it already bound? @@ -466,6 +467,7 @@ proc semTuple(c: PContext, n: PNode, prev: PType): PType = addSon(result.n, newSymNode(field)) addSonSkipIntLit(result, typ) styleCheckDef(c.config, a.sons[j].info, field) + onDef(field.info, field) if result.n.len == 0: result.n = nil proc semIdentVis(c: PContext, kind: TSymKind, n: PNode, @@ -688,7 +690,6 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int, else: rectype.sym for i in countup(0, sonsLen(n)-3): var f = semIdentWithPragma(c, skField, n.sons[i], {sfExported}) - styleCheckDef(c.config, n.sons[i].info, f) suggestSym(c.config, n.sons[i].info, f, c.graph.usageSym) f.typ = typ f.position = pos @@ -703,6 +704,7 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int, if a.kind == nkEmpty: addSon(father, newSymNode(f)) else: addSon(a, newSymNode(f)) styleCheckDef(c.config, f) + onDef(f.info, f) if a.kind != nkEmpty: addSon(father, a) of nkSym: # This branch only valid during generic object @@ -988,7 +990,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, of tyGenericParam: markUsed(c.config, info, paramType.sym, c.graph.usageSym) - styleCheckUse(info, paramType.sym) + onUse(info, paramType.sym) if tfWildcard in paramType.flags: paramType.flags.excl tfWildcard paramType.sym.kind = skType @@ -1110,6 +1112,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, rawAddSon(result, finalType) addParamOrResult(c, arg, kind) styleCheckDef(c.config, a.sons[j].info, arg) + onDef(a[j].info, arg) var r: PType if n.sons[0].kind != nkEmpty: @@ -1621,8 +1624,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = result = prev of nkSym: let s = getGenSym(c, n.sym) - if s.kind == skType and s.typ != nil or - s.kind == skParam and s.typ.kind == tyTypeDesc: + if s.typ != nil and (s.kind == skType or s.typ.kind == tyTypeDesc): var t = if s.kind == skType: s.typ @@ -1638,7 +1640,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = assignType(prev, t) result = prev markUsed(c.config, n.info, n.sym, c.graph.usageSym) - styleCheckUse(n.info, n.sym) + onUse(n.info, n.sym) else: if s.kind != skError: localError(c.config, n.info, errTypeExpected) result = newOrPrevType(tyError, prev, c) @@ -1745,10 +1747,8 @@ proc processMagicType(c: PContext, m: PSym) = setMagicType(c.config, m, tyAnything, 0) else: setMagicType(c.config, m, tyExpr, 0) - if m.name.s == "expr": m.typ.flags.incl tfOldSchoolExprStmt of mStmt: setMagicType(c.config, m, tyStmt, 0) - if m.name.s == "stmt": m.typ.flags.incl tfOldSchoolExprStmt of mTypeDesc, mType: setMagicType(c.config, m, tyTypeDesc, 0) rawAddSon(m.typ, newTypeS(tyNone, c)) diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index be6ffc586..b05fb37ae 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -30,11 +30,6 @@ proc checkConstructedType*(conf: ConfigRef; info: TLineInfo, typ: PType) = localError(conf, info, "type 'var var' is not allowed") elif computeSize(conf, t) == szIllegalRecursion: localError(conf, info, "illegal recursion in type '" & typeToString(t) & "'") - - t = typ.skipTypes({tyGenericInst}) - if t.kind == tyArray and tfUncheckedArray in t.flags: - t[0].flags.incl tfUncheckedArray # mark range of unchecked array also unchecked - when false: if t.kind == tyObject and t.sons[0] != nil: if t.sons[0].kind != tyObject or tfFinal in t.sons[0].flags: diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 4dc7690ef..4adf0bed3 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -13,9 +13,9 @@ import intsets, ast, astalgo, semdata, types, msgs, renderer, lookups, semtypinst, magicsys, condsyms, idents, lexer, options, parampatterns, strutils, trees, - linter, lineinfos + linter, lineinfos, lowerings, modulegraphs -when defined(booting) or defined(nimsuggest): +when (defined(booting) or defined(nimsuggest)) and not defined(leanCompiler): import docgen type @@ -1210,9 +1210,7 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType, if f.kind == tyVarargs: if tfVarargs in a.flags: return typeRel(c, f.base, a.lastSon) - if tfOldSchoolExprStmt in f.sons[0].flags: - if f.sons[0].kind == tyExpr: return - elif f.sons[0].kind == tyStmt: return + if f.sons[0].kind == tyStmt: return template matchArrayOrSeq(aBase: PType) = let ff = f.base @@ -1758,7 +1756,7 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType, result = isNone of tyStmt: - if aOrig != nil and tfOldSchoolExprStmt notin f.flags: + if aOrig != nil: put(c, f, aOrig) result = isGeneric @@ -1857,8 +1855,14 @@ proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType, else: param = copyTree(arg) addSon(result, param) + + if dest.kind in {tyVar, tyLent}: + dest.flags.incl tfVarIsPtr + result = newDeref(result) + inc(m.convMatches) - m.genericConverter = srca == isGeneric or destIsGeneric + if m.genericConverter == false: + m.genericConverter = srca == isGeneric or destIsGeneric return result proc localConvMatch(c: PContext, m: var TCandidate, f, a: PType, @@ -2151,7 +2155,7 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType, else: # only one valid interpretation found: markUsed(m.c.config, arg.info, arg.sons[best].sym, m.c.graph.usageSym) - styleCheckUse(arg.info, arg.sons[best].sym) + onUse(arg.info, arg.sons[best].sym) result = paramTypesMatchAux(m, f, arg.sons[best].typ, arg.sons[best], argOrig) when false: @@ -2215,8 +2219,7 @@ proc incrIndexType(t: PType) = inc t.sons[0].n.sons[1].intVal template isVarargsUntyped(x): untyped = - x.kind == tyVarargs and x.sons[0].kind == tyExpr and - tfOldSchoolExprStmt notin x.sons[0].flags + x.kind == tyVarargs and x.sons[0].kind == tyExpr proc matchesAux(c: PContext, n, nOrig: PNode, m: var TCandidate, marker: var IntSet) = diff --git a/compiler/sizealignoffsetimpl.nim b/compiler/sizealignoffsetimpl.nim index 573b27094..4791788fa 100644 --- a/compiler/sizealignoffsetimpl.nim +++ b/compiler/sizealignoffsetimpl.nim @@ -1,3 +1,12 @@ +# +# +# The Nim Compiler +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# +## code owner: Arne Döring +## e-mail: arne.doering@gmx.net proc align(address, alignment: BiggestInt): BiggestInt = result = (address + (alignment - 1)) and not (alignment - 1) @@ -118,9 +127,14 @@ proc computeObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode, initialOffset: result.offset = align(offset, result.align) of nkSym: - computeSizeAlign(conf, n.sym.typ) - let size = n.sym.typ.size - let align = n.sym.typ.align + var size = szUnknownSize + var align = szUnknownSize + + if n.sym.bitsize == 0: # 0 represents bitsize not set + computeSizeAlign(conf, n.sym.typ) + size = n.sym.typ.size.int + align = n.sym.typ.align.int + result.align = align if initialOffset == szUnknownSize or size == szUnknownSize: n.sym.offset = szUnknownSize @@ -309,16 +323,24 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) = var headerAlign: int16 if typ.sons[0] != nil: # compute header size - var st = typ.sons[0] - while st.kind in skipPtrs: - st = st.sons[^1] - computeSizeAlign(conf, st) - if st.size == szIllegalRecursion: - typ.size = st.size - typ.align = st.align - return - headerSize = st.size - headerAlign = st.align + + if conf.cmd == cmdCompileToCpp: + # if the target is C++ the members of this type are written + # into the padding byets at the end of the parent type. At the + # moment it is not supported to calculate that. + headerSize = szUnknownSize + headerAlign = szUncomputedSize + else: + var st = typ.sons[0] + while st.kind in skipPtrs: + st = st.sons[^1] + computeSizeAlign(conf, st) + if st.size == szIllegalRecursion: + typ.size = st.size + typ.align = st.align + return + headerSize = st.size + headerAlign = st.align elif isObjectWithTypeFieldPredicate(typ): # this branch is taken for RootObj headerSize = conf.target.intSize diff --git a/compiler/suggest.nim b/compiler/suggest.nim index b264415d8..dfa6e5ddb 100644 --- a/compiler/suggest.nim +++ b/compiler/suggest.nim @@ -115,7 +115,7 @@ proc symToSuggest(conf: ConfigRef; s: PSym, isLocal: bool, section: IdeCmd, info result.forth = typeToString(s.typ) else: result.forth = "" - when defined(nimsuggest) and not defined(noDocgen): + when defined(nimsuggest) and not defined(noDocgen) and not defined(leanCompiler): result.doc = s.extractDocComment let infox = if section in {ideUse, ideHighlight, ideOutline}: info else: s.info result.filePath = toFullPath(conf, infox) @@ -153,7 +153,7 @@ proc `$`*(suggest: Suggest): string = result.add(sep) result.add($suggest.column) result.add(sep) - when defined(nimsuggest) and not defined(noDocgen): + when defined(nimsuggest) and not defined(noDocgen) and not defined(leanCompiler): result.add(suggest.doc.escape) if suggest.version == 0: result.add(sep) diff --git a/compiler/syntaxes.nim b/compiler/syntaxes.nim index decdf1715..60246b9bf 100644 --- a/compiler/syntaxes.nim +++ b/compiler/syntaxes.nim @@ -17,10 +17,10 @@ type TFilterKind* = enum filtNone, filtTemplate, filtReplace, filtStrip TParserKind* = enum - skinStandard, skinStrongSpaces, skinEndX + skinStandard, skinEndX const - parserNames*: array[TParserKind, string] = ["standard", "strongspaces", + parserNames*: array[TParserKind, string] = ["standard", "endx"] filterNames*: array[TFilterKind, string] = ["none", "stdtmpl", "replace", "strip"] @@ -34,14 +34,14 @@ template config(p: TParsers): ConfigRef = p.parser.lex.config proc parseAll*(p: var TParsers): PNode = case p.skin - of skinStandard, skinStrongSpaces: + of skinStandard: result = parser.parseAll(p.parser) of skinEndX: internalError(p.config, "parser to implement") proc parseTopLevelStmt*(p: var TParsers): PNode = case p.skin - of skinStandard, skinStrongSpaces: + of skinStandard: result = parser.parseTopLevelStmt(p.parser) of skinEndX: internalError(p.config, "parser to implement") @@ -153,21 +153,23 @@ proc openParsers*(p: var TParsers, fileIdx: FileIndex, inputstream: PLLStream; else: s = inputstream case p.skin of skinStandard, skinEndX: - parser.openParser(p.parser, fileIdx, s, cache, config, false) - of skinStrongSpaces: - parser.openParser(p.parser, fileIdx, s, cache, config, true) + parser.openParser(p.parser, fileIdx, s, cache, config) proc closeParsers*(p: var TParsers) = parser.closeParser(p.parser) -proc parseFile*(fileIdx: FileIndex; cache: IdentCache; config: ConfigRef): PNode {.procvar.} = - var - p: TParsers - f: File +proc setupParsers*(p: var TParsers; fileIdx: FileIndex; cache: IdentCache; + config: ConfigRef): bool = + var f: File let filename = toFullPathConsiderDirty(config, fileIdx) if not open(f, filename.string): rawMessage(config, errGenerated, "cannot open file: " & filename.string) - return + return false openParsers(p, fileIdx, llStreamOpen(f), cache, config) - result = parseAll(p) - closeParsers(p) + result = true + +proc parseFile*(fileIdx: FileIndex; cache: IdentCache; config: ConfigRef): PNode {.procvar.} = + var p: TParsers + if setupParsers(p, fileIdx, cache, config): + result = parseAll(p) + closeParsers(p) diff --git a/compiler/transf.nim b/compiler/transf.nim index 800d56b3a..7b2979dea 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -24,7 +24,8 @@ import sempass2, lowerings, destroyer, liftlocals, modulegraphs, lineinfos -proc transformBody*(g: ModuleGraph, prc: PSym, cache = true): PNode +proc transformBody*(g: ModuleGraph, prc: PSym, cache = true; + noDestructors = false): PNode import closureiters, lambdalifting @@ -48,7 +49,7 @@ type inlining: int # > 0 if we are in inlining context (copy vars) nestedProcs: int # > 0 if we are in a nested proc contSyms, breakSyms: seq[PSym] # to transform 'continue' and 'break' - deferDetected, tooEarly, needsDestroyPass: bool + deferDetected, tooEarly, needsDestroyPass, noDestructors: bool graph: ModuleGraph PTransf = ref TTransfContext @@ -130,7 +131,7 @@ proc transformSymAux(c: PTransf, n: PNode): PNode = let s = n.sym if s.typ != nil and s.typ.callConv == ccClosure: if s.kind in routineKinds: - discard transformBody(c.graph, s) + discard transformBody(c.graph, s, true, c.noDestructors) if s.kind == skIterator: if c.tooEarly: return n else: return liftIterSym(c.graph, n, getCurrOwner(c)) @@ -159,7 +160,7 @@ proc transformSymAux(c: PTransf, n: PNode): PNode = return tc = tc.next result = b - + proc transformSym(c: PTransf, n: PNode): PTransNode = result = PTransNode(transformSymAux(c, n)) @@ -243,7 +244,7 @@ proc newLabel(c: PTransf, n: PNode): PSym = result.name = getIdent(c.graph.cache, genPrefix & $result.id) proc transformBlock(c: PTransf, n: PNode): PTransNode = - var labl: PSym + var labl: PSym if c.inlining > 0: labl = newLabel(c, n[0]) idNodeTablePut(c.transCon.mapping, n[0].sym, newSymNode(labl)) @@ -611,7 +612,7 @@ proc transformFor(c: PTransf, n: PNode): PTransNode = add(stmtList, newAsgnStmt(c, nkFastAsgn, temp, arg.PTransNode)) idNodeTablePut(newC.mapping, formal, temp) - let body = transformBody(c.graph, iter) + let body = transformBody(c.graph, iter, true, c.noDestructors) pushInfoContext(c.graph.config, n.info) inc(c.inlining) add(stmtList, transform(c, body)) @@ -1029,7 +1030,8 @@ template liftDefer(c, root) = if c.deferDetected: liftDeferAux(root) -proc transformBody*(g: ModuleGraph, prc: PSym, cache = true): PNode = +proc transformBody*(g: ModuleGraph, prc: PSym, cache = true; + noDestructors = false): PNode = assert prc.kind in routineKinds if prc.transformedBody != nil: @@ -1037,22 +1039,22 @@ proc transformBody*(g: ModuleGraph, prc: PSym, cache = true): PNode = elif nfTransf in prc.ast[bodyPos].flags or prc.kind in {skTemplate}: result = prc.ast[bodyPos] else: - prc.transformedBody = newNode(nkEmpty) # protects from recursion var c = openTransf(g, prc.getModule, "") + c.noDestructors = noDestructors result = liftLambdas(g, prc, prc.ast[bodyPos], c.tooEarly) result = processTransf(c, result, prc) liftDefer(c, result) result = liftLocalsIfRequested(prc, result, g.cache, g.config) - if c.needsDestroyPass: #and newDestructors: + if c.needsDestroyPass and not noDestructors: result = injectDestructorCalls(g, prc, result) if prc.isIterator: result = g.transformClosureIterator(prc, result) - + incl(result.flags, nfTransf) - let cache = cache or prc.typ.callConv == ccInline + let cache = cache or prc.typ.callConv == ccInline if cache: # genProc for inline procs will be called multiple times from diffrent modules, # it is important to transform exactly once to get sym ids and locations right @@ -1072,7 +1074,8 @@ proc transformStmt*(g: ModuleGraph; module: PSym, n: PNode): PNode = result = injectDestructorCalls(g, module, result) incl(result.flags, nfTransf) -proc transformExpr*(g: ModuleGraph; module: PSym, n: PNode): PNode = +proc transformExpr*(g: ModuleGraph; module: PSym, n: PNode; + noDestructors = false): PNode = if nfTransf in n.flags: result = n else: @@ -1081,6 +1084,6 @@ proc transformExpr*(g: ModuleGraph; module: PSym, n: PNode): PNode = liftDefer(c, result) # expressions are not to be injected with destructor calls as that # the list of top level statements needs to be collected before. - if c.needsDestroyPass: + if c.needsDestroyPass and not noDestructors: result = injectDestructorCalls(g, module, result) incl(result.flags, nfTransf) diff --git a/compiler/types.nim b/compiler/types.nim index 5b14015c1..b163ca4e9 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -1086,8 +1086,8 @@ proc inheritanceDiff*(a, b: PType): int = # | returns: +x iff `a` is the x'th direct subclass of `b` # | returns: `maxint` iff `a` and `b` are not compatible at all if a == b or a.kind == tyError or b.kind == tyError: return 0 - assert a.kind == tyObject - assert b.kind == tyObject + assert a.kind in {tyObject} + skipPtrs + assert b.kind in {tyObject} + skipPtrs var x = a result = 0 while x != nil: diff --git a/compiler/vm.nim b/compiler/vm.nim index 05ee3b90e..7e7ec8903 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -503,7 +503,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of opcAsgnFloat64FromInt: let rb = instr.regB ensureKind(rkFloat) - regs[ra].floatVal = cast[float64](int64(regs[rb].intVal)) + regs[ra].floatVal = cast[float64](int64(regs[rb].intVal)) of opcAsgnComplex: asgnComplex(regs[ra], regs[instr.regB]) of opcAsgnRef: @@ -945,10 +945,10 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = let a = regs[rb].node if a.kind == nkSym: regs[ra].node = if a.sym.ast.isNil: newNode(nkNilLit) - else: + else: let ast = a.sym.ast.shallowCopy for i in 0..<a.sym.ast.len: - ast[i] = a.sym.ast[i] + ast[i] = a.sym.ast[i] ast[bodyPos] = transformBody(c.graph, a.sym) ast.copyTree() of opcSymOwner: @@ -960,6 +960,17 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = regs[ra].node.flags.incl nfIsRef else: stackTrace(c, tos, pc, "node is not a symbol") + of opcSymIsInstantiationOf: + decodeBC(rkInt) + let a = regs[rb].node + let b = regs[rc].node + if a.kind == nkSym and a.sym.kind in skProcKinds and + b.kind == nkSym and b.sym.kind in skProcKinds: + regs[ra].intVal = + if sfFromGeneric in a.sym.flags and a.sym.owner == b.sym: 1 + else: 0 + else: + stackTrace(c, tos, pc, "node is not a proc symbol") of opcEcho: let rb = instr.regB if rb == 1: @@ -1228,7 +1239,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of opcOf: decodeBC(rkInt) let typ = c.types[regs[rc].intVal.int] - regs[ra].intVal = ord(inheritanceDiff(regs[rb].node.typ, typ) >= 0) + regs[ra].intVal = ord(inheritanceDiff(regs[rb].node.typ, typ) <= 0) of opcIs: decodeBC(rkInt) let t1 = regs[rb].node.typ.skipTypes({tyTypeDesc}) @@ -1822,7 +1833,7 @@ proc execProc*(c: PCtx; sym: PSym; args: openArray[PNode]): PNode = "NimScript: attempt to call non-routine: " & sym.name.s) proc evalStmt*(c: PCtx, n: PNode) = - let n = transformExpr(c.graph, c.module, n) + let n = transformExpr(c.graph, c.module, n, noDestructors = true) let start = genStmt(c, n) # execute new instructions; this redundant opcEof check saves us lots # of allocations in 'execute': @@ -1830,7 +1841,7 @@ proc evalStmt*(c: PCtx, n: PNode) = discard execute(c, start) proc evalExpr*(c: PCtx, n: PNode): PNode = - let n = transformExpr(c.graph, c.module, n) + let n = transformExpr(c.graph, c.module, n, noDestructors = true) let start = genExpr(c, n) assert c.code[start].opcode != opcEof result = execute(c, start) @@ -1877,7 +1888,7 @@ const evalPass* = makePass(myOpen, myProcess, myClose) proc evalConstExprAux(module: PSym; g: ModuleGraph; prc: PSym, n: PNode, mode: TEvalMode): PNode = - let n = transformExpr(g, module, n) + let n = transformExpr(g, module, n, noDestructors = true) setupGlobalCtx(module, g) var c = PCtx g.vm let oldMode = c.mode diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim index 25ace3cdd..493078f74 100644 --- a/compiler/vmdef.nim +++ b/compiler/vmdef.nim @@ -147,7 +147,8 @@ type opcTypeTrait, opcMarshalLoad, opcMarshalStore, opcToNarrowInt, - opcSymOwner + opcSymOwner, + opcSymIsInstantiationOf TBlock* = object label*: PSym diff --git a/compiler/vmdeps.nim b/compiler/vmdeps.nim index 8aac5fb87..f160a3096 100644 --- a/compiler/vmdeps.nim +++ b/compiler/vmdeps.nim @@ -157,7 +157,10 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo; of tyObject: if inst: result = newNodeX(nkObjectTy) - result.add t.sym.ast[2][0].copyTree # copy object pragmas + if t.sym.ast != nil: + result.add t.sym.ast[2][0].copyTree # copy object pragmas + else: + result.add newNodeI(nkEmpty, info) if t.sons[0] == nil: result.add newNodeI(nkEmpty, info) else: # handle parent object diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index ea0fb35ff..e52f460d2 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -182,7 +182,10 @@ const HighRegisterPressure = 40 proc bestEffort(c: PCtx): TLineInfo = - (if c.prc == nil: c.module.info else: c.prc.sym.info) + if c.prc != nil and c.prc.sym != nil: + c.prc.sym.info + else: + c.module.info proc getTemp(cc: PCtx; tt: PType): TRegister = let typ = tt.skipTypesOrNil({tyStatic}) @@ -776,7 +779,7 @@ proc genCastIntFloat(c: PCtx; n: PNode; dest: var TDest) = let src = n.sons[1].typ.skipTypes(abstractRange)#.kind let dst = n.sons[0].typ.skipTypes(abstractRange)#.kind let src_size = getSize(c.config, src) - let dst_size = getSize(c.config, dst) + let dst_size = getSize(c.config, dst) if c.config.target.intSize < 8: signedIntegers.incl(tyInt) unsignedIntegers.incl(tyUInt) @@ -820,7 +823,7 @@ proc genCastIntFloat(c: PCtx; n: PNode; dest: var TDest) = c.freeTemp(tmp) elif src_size == dst_size and src.kind in {tyFloat, tyFloat32, tyFloat64} and - dst.kind in allowedIntegers: + dst.kind in allowedIntegers: let tmp = c.genx(n[1]) if dest < 0: dest = c.getTemp(n[0].typ) if src.kind == tyFloat32: @@ -1095,7 +1098,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = var tmp = c.genx(n.sons[1]) var idx = c.getTemp(getSysType(c.graph, n.info, tyInt)) var typ = n.sons[2].typ - if m == mOf: typ = typ.skipTypes(abstractPtrs-{tyTypeDesc}) + if m == mOf: typ = typ.skipTypes(abstractPtrs) c.gABx(n, opcLdImmInt, idx, c.genType(typ)) c.gABC(n, if m == mOf: opcOf else: opcIs, dest, tmp, idx) c.freeTemp(tmp) @@ -1112,13 +1115,14 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = of mEcho: unused(c, n, dest) let n = n[1].skipConv - let x = c.getTempRange(n.len, slotTempUnknown) - internalAssert c.config, n.kind == nkBracket - for i in 0..<n.len: - var r: TRegister = x+i - c.gen(n.sons[i], r) - c.gABC(n, opcEcho, x, n.len) - c.freeTempRange(x, n.len) + if n.kind == nkBracket: + # can happen for nim check, see bug #9609 + let x = c.getTempRange(n.len, slotTempUnknown) + for i in 0..<n.len: + var r: TRegister = x+i + c.gen(n.sons[i], r) + c.gABC(n, opcEcho, x, n.len) + c.freeTempRange(x, n.len) of mAppendStrCh: unused(c, n, dest) genBinaryStmtVar(c, n, opcAddStrCh) @@ -1144,6 +1148,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = of mGetImpl: genUnaryABC(c, n, dest, opcGetImpl) of mGetImplTransf: genUnaryABC(c, n, dest, opcGetImplTransf) of mSymOwner: genUnaryABC(c, n, dest, opcSymOwner) + of mSymIsInstantiationOf: genBinaryABC(c, n, dest, opcSymIsInstantiationOf) of mNChild: genBinaryABC(c, n, dest, opcNChild) of mNSetChild: genVoidABC(c, n, dest, opcNSetChild) of mNDel: genVoidABC(c, n, dest, opcNDel) @@ -2150,7 +2155,8 @@ proc genProc(c: PCtx; s: PSym): int = s.ast.sons[miscPos] = x # thanks to the jmp we can add top level statements easily and also nest # procs easily: - let body = transformBody(c.graph, s, cache = not isCompileTimeProc(s)) + let body = transformBody(c.graph, s, cache = not isCompileTimeProc(s), + noDestructors = true) let procStart = c.xjmp(body, opcJmp, 0) var p = PProc(blocks: @[], sym: s) let oldPrc = c.prc diff --git a/compiler/vmops.nim b/compiler/vmops.nim index 83e65279a..f87ab4508 100644 --- a/compiler/vmops.nim +++ b/compiler/vmops.nim @@ -11,7 +11,7 @@ #import vmdeps, vm from math import sqrt, ln, log10, log2, exp, round, arccos, arcsin, arctan, arctan2, cos, cosh, hypot, sinh, sin, tan, tanh, pow, trunc, - floor, ceil, fmod + floor, ceil, `mod`, fmod from os import getEnv, existsEnv, dirExists, fileExists, putEnv, walkDir @@ -21,9 +21,6 @@ template mathop(op) {.dirty.} = template osop(op) {.dirty.} = registerCallback(c, "stdlib.os." & astToStr(op), `op Wrapper`) -template ospathsop(op) {.dirty.} = - registerCallback(c, "stdlib.ospaths." & astToStr(op), `op Wrapper`) - template systemop(op) {.dirty.} = registerCallback(c, "stdlib.system." & astToStr(op), `op Wrapper`) @@ -107,10 +104,14 @@ proc registerAdditionalOps*(c: PCtx) = wrap1f_math(ceil) wrap2f_math(fmod) + proc `mod Wrapper`(a: VmArgs) {.nimcall.} = + setResult(a, `mod`(getFloat(a, 0), getFloat(a, 1))) + registerCallback(c, "stdlib.math.mod", `mod Wrapper`) + when defined(nimcore): - wrap2s(getEnv, ospathsop) - wrap1s(existsEnv, ospathsop) - wrap2svoid(putEnv, ospathsop) + wrap2s(getEnv, osop) + wrap1s(existsEnv, osop) + wrap2svoid(putEnv, osop) wrap1s(dirExists, osop) wrap1s(fileExists, osop) wrap2svoid(writeFile, systemop) diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim index 800b5dc18..41bdc9fcb 100644 --- a/compiler/wordrecg.nim +++ b/compiler/wordrecg.nim @@ -69,13 +69,13 @@ type wImplicitStatic, wGlobal, wCodegenDecl, wUnchecked, wGuard, wLocks, wPartial, wExplain, wLiftLocals, - wAuto, wBool, wCatch, wChar, wClass, + wAuto, wBool, wCatch, wChar, wClass, wCompl wConst_cast, wDefault, wDelete, wDouble, wDynamic_cast, wExplicit, wExtern, wFalse, wFloat, wFriend, wGoto, wInt, wLong, wMutable, wNamespace, wNew, wOperator, - wPrivate, wProtected, wPublic, wRegister, wReinterpret_cast, + wPrivate, wProtected, wPublic, wRegister, wReinterpret_cast, wRestrict, wShort, wSigned, wSizeof, wStatic_cast, wStruct, wSwitch, - wThis, wThrow, wTrue, wTypedef, wTypeid, wTypename, + wThis, wThrow, wTrue, wTypedef, wTypeid, wTypeof, wTypename, wUnion, wPacked, wUnsigned, wVirtual, wVoid, wVolatile, wWchar_t, wAlignas, wAlignof, wConstexpr, wDecltype, wNullptr, wNoexcept, @@ -156,14 +156,14 @@ const "asmnostackframe", "implicitstatic", "global", "codegendecl", "unchecked", "guard", "locks", "partial", "explain", "liftlocals", - "auto", "bool", "catch", "char", "class", + "auto", "bool", "catch", "char", "class", "compl", "const_cast", "default", "delete", "double", "dynamic_cast", "explicit", "extern", "false", "float", "friend", "goto", "int", "long", "mutable", "namespace", "new", "operator", - "private", "protected", "public", "register", "reinterpret_cast", + "private", "protected", "public", "register", "reinterpret_cast", "restrict", "short", "signed", "sizeof", "static_cast", "struct", "switch", - "this", "throw", "true", "typedef", "typeid", + "this", "throw", "true", "typedef", "typeid", "typeof", "typename", "union", "packed", "unsigned", "virtual", "void", "volatile", "wchar_t", diff --git a/doc/advopt.txt b/doc/advopt.txt index 252067129..7cd72f6c3 100644 --- a/doc/advopt.txt +++ b/doc/advopt.txt @@ -29,6 +29,9 @@ Advanced options: produce hints or errors for Nim identifiers that do not adhere to Nim's official style guide https://nim-lang.org/docs/nep1.html + --showAllMismatches:on|off + show all mismatching candidates in overloading + resolution --lib:PATH set the system library path --import:PATH add an automatically imported module --include:PATH add an automatically included module @@ -98,6 +101,7 @@ Advanced options: --listCmd list the commands used to execute external programs --parallelBuild:0|1|... perform a parallel build value = number of processors (0 for auto-detect) + --incremental:on|off only recompile the changed modules (experimental!) --verbosity:0|1|2|3 set Nim's verbosity level (1 is default) --experimental:$1 enable experimental language feature diff --git a/doc/codeowners.rst b/doc/codeowners.rst index 0758e3ffb..129c47cfc 100644 --- a/doc/codeowners.rst +++ b/doc/codeowners.rst @@ -51,7 +51,7 @@ async dom96 strutils araq sequtils dom96, araq times GULPF -os, ospaths dom96, araq +os dom96, araq re araq nre flaviu math, fenv krux02, cooldome diff --git a/doc/contributing.rst b/doc/contributing.rst index 84b15f419..a2c95db74 100644 --- a/doc/contributing.rst +++ b/doc/contributing.rst @@ -22,7 +22,7 @@ There are 3 types of tests: 2. tests in ``when isMainModule:`` block, ran by ``nim c mymod.nim`` ``nimble test`` also typially runs these in external nimble packages. -3. testament tests, eg: tests/stdlib/tospaths.nim (only used for Nim repo). +3. testament tests, eg: tests/stdlib/tos.nim (only used for Nim repo). Not all the tests follow the convention here, feel free to change the ones that don't. Always leave the code cleaner than you found it. @@ -30,7 +30,7 @@ that don't. Always leave the code cleaner than you found it. Stdlib ------ -If you change the stdlib (anything under ``lib/``, eg ``lib/pure/ospaths.nim``), +If you change the stdlib (anything under ``lib/``, eg ``lib/pure/os.nim``), put a test in the file you changed. Add the tests under a ``when isMainModule:`` condition so they only get executed when the tester is building the file. Each test should be in a separate ``block:`` statement, such that @@ -53,7 +53,7 @@ Sample test: doAssert: not 1 == 2 Newer tests tend to be run via ``testament`` rather than via ``when isMainModule:``, -eg ``tests/stdlib/tospaths.nim``; this allows additional features such as custom +eg ``tests/stdlib/tos.nim``; this allows additional features such as custom compiler flags; for more details see below. Compiler @@ -197,7 +197,7 @@ as well as ``testament`` and guarantee they stay in sync. result = a & "Bar" -See `parentDir <https://nim-lang.github.io/Nim/ospaths.html#parentDir%2Cstring>`_ +See `parentDir <https://nim-lang.github.io/Nim/os.html#parentDir%2Cstring>`_ example. The RestructuredText Nim uses has a special syntax for including code snippets diff --git a/doc/filters.rst b/doc/filters.rst index e8106749e..40346ecaf 100644 --- a/doc/filters.rst +++ b/doc/filters.rst @@ -31,13 +31,16 @@ Usage ===== First, put your SCF code in a separate file with filters specified in the first line. -**Note:** You can name your SCF file with any file extension you want, but the conventional extension is ``.tmpl``. +**Note:** You can name your SCF file with any file extension you want, but the +conventional extension is ``.nimf`` +(it used to be ``.tmpl`` but that was too generic, for example preventing github to +recognize it as Nim source file). -If we use `generateXML` code shown above and call the SCF file `xmlGen.tmpl` +If we use `generateXML` code shown above and call the SCF file `xmlGen.nimf` In your `main.nim`: .. code-block:: nim - include "xmlGen.tmpl" + include "xmlGen.nimf" echo generateXML("John Smith","42") diff --git a/doc/grammar.txt b/doc/grammar.txt index e06ebd5d9..b5aa6fd5a 100644 --- a/doc/grammar.txt +++ b/doc/grammar.txt @@ -9,7 +9,7 @@ operator = OP0 | OP1 | OP2 | OP3 | OP4 | OP5 | OP6 | OP7 | OP8 | OP9 | 'is' | 'isnot' | 'in' | 'notin' | 'of' | 'div' | 'mod' | 'shl' | 'shr' | 'not' | 'static' | '..' prefixOperator = operator -optInd = COMMENT? +optInd = COMMENT? IND? optPar = (IND{>} | IND{=})? simpleExpr = arrowExpr (OP0 optInd arrowExpr)* pragma? arrowExpr = assignExpr (OP1 optInd assignExpr)* @@ -85,17 +85,19 @@ paramListColon = paramList? (':' optInd typeDesc)? doBlock = 'do' paramListArrow pragmas? colcom stmt procExpr = 'proc' paramListColon pragmas? ('=' COMMENT? stmt)? distinct = 'distinct' optInd typeDesc +forStmt = 'for' (identWithPragma ^+ comma) 'in' expr colcom stmt +forExpr = forStmt expr = (blockExpr | ifExpr | whenExpr | caseExpr + | forExpr | tryExpr) / simpleExpr typeKeyw = 'var' | 'out' | 'ref' | 'ptr' | 'shared' | 'tuple' | 'proc' | 'iterator' | 'distinct' | 'object' | 'enum' primary = typeKeyw typeDescK / prefixOperator* identOrLiteral primarySuffix* - / 'static' primary / 'bind' primary typeDesc = simpleExpr typeDefAux = simpleExpr @@ -142,12 +144,11 @@ tryExpr = 'try' colcom stmt &(optInd 'except'|'finally') (optInd 'except' exprList colcom stmt)* (optInd 'finally' colcom stmt)? exceptBlock = 'except' colcom stmt -forStmt = 'for' (identWithPragma ^+ comma) 'in' expr colcom stmt blockStmt = 'block' symbol? colcom stmt blockExpr = 'block' symbol? colcom stmt staticStmt = 'static' colcom stmt deferStmt = 'defer' colcom stmt -asmStmt = 'asm' pragma? (STR_LIT | RSTR_LIT | TRIPLE_STR_LIT) +asmStmt = 'asm' pragma? (STR_LIT | RSTR_LIT | TRIPLESTR_LIT) genericParam = symbol (comma symbol)* (colon expr)? ('=' optInd expr)? genericParamList = '[' optInd genericParam ^* (comma/semicolon) optPar ']' diff --git a/doc/intern.rst b/doc/intern.rst index 0fa4fd7b6..b71ad592f 100644 --- a/doc/intern.rst +++ b/doc/intern.rst @@ -31,7 +31,7 @@ Path Purpose reStructuredText files ``lib`` the Nim library ``web`` website of Nim; generated by ``nimweb`` - from the ``*.txt`` and ``*.tmpl`` files + from the ``*.txt`` and ``*.nimf`` files ============ =================================================== diff --git a/doc/lib.rst b/doc/lib.rst index 89ab26d81..46e1f9d19 100644 --- a/doc/lib.rst +++ b/doc/lib.rst @@ -147,10 +147,13 @@ String handling * `subexes <subexes.html>`_ This module implements advanced string substitution operations. -* `editdistance <editdistance>`_ +* `std/editdistance <editdistance.html>`_ This module contains an algorithm to compute the edit distance between two Unicode strings. +* `std/wordwrap <wordwrap.html>`_ + This module contains an algorithm to wordwrap a Unicode string. + Generic Operating System Services --------------------------------- diff --git a/doc/manual.rst b/doc/manual.rst index 98943619d..b39711dfb 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -514,6 +514,9 @@ are used for other notational purposes. ``*:`` is as a special case treated as the two tokens `*`:tok: and `:`:tok: (to support ``var v*: T``). +The ``not`` keyword is always a unary operator, ``a not b`` is parsed +as ``a(not b)``, not as ``(a) not (b)``. + Other tokens ------------ @@ -1231,6 +1234,37 @@ so that the builtin ``echo`` proc does what is expected: # prints "@[1, 2, 3]" and not "123" +Unchecked arrays +---------------- +The ``UncheckedArray[T]`` type is a special kind of ``array`` where its bounds +are not checked. This is often useful to implement customized flexibly sized +arrays. Additionally an unchecked array is translated into a C array of +undetermined size: + +.. code-block:: nim + type + MySeq = object + len, cap: int + data: UncheckedArray[int] + +Produces roughly this C code: + +.. code-block:: C + typedef struct { + NI len; + NI cap; + NI data[]; + } MySeq; + +The base type of the unchecked array may not contain any GC'ed memory but this +is currently not checked. + +**Future directions**: GC'ed memory should be allowed in unchecked arrays and +there should be an explicit annotation of how the GC is to determine the +runtime size of the array. + + + Tuples and object types ----------------------- A variable of a tuple or object type is a heterogeneous storage @@ -2349,7 +2383,8 @@ Automatic self insertions Starting with version 0.14 of the language, Nim supports ``field`` as a shortcut for ``self.field`` comparable to the `this`:idx: keyword in Java or C++. This feature has to be explicitly enabled via a ``{.this: self.}`` -statement pragma. This pragma is active for the rest of the module: +statement pragma (instead of ``self`` any other identifier can be used too). +This pragma is active for the rest of the module: .. code-block:: nim type @@ -2364,10 +2399,6 @@ statement pragma. This pragma is active for the rest of the module: # is rewritten to: # result = self.parentField + self.childField -Instead of ``self`` any other identifier can be used too, but -``{.this: self.}`` will become the default directive for the whole language -eventually. - In addition to fields, routine applications are also rewritten, but only if no other interpretation of the call is possible: @@ -7897,36 +7928,6 @@ defined, and it should not be used with GC'ed memory (ref's). **Future directions**: Using GC'ed memory in packed pragma will result in compile-time error. Usage with inheritance should be defined and documented. -Unchecked pragma ----------------- -The ``unchecked`` pragma can be used to mark a named array as ``unchecked`` -meaning its bounds are not checked. This is often useful to -implement customized flexibly sized arrays. Additionally an unchecked array is -translated into a C array of undetermined size: - -.. code-block:: nim - type - ArrayPart{.unchecked.} = array[0, int] - MySeq = object - len, cap: int - data: ArrayPart - -Produces roughly this C code: - -.. code-block:: C - typedef struct { - NI len; - NI cap; - NI data[]; - } MySeq; - -The base type of the unchecked array may not contain any GC'ed memory but this -is currently not checked. - -**Future directions**: GC'ed memory should be allowed in unchecked arrays and -there should be an explicit annotation of how the GC is to determine the -runtime size of the array. - Dynlib pragma for import ------------------------ diff --git a/doc/nims.rst b/doc/nims.rst index dea09e1e8..034ad1fda 100644 --- a/doc/nims.rst +++ b/doc/nims.rst @@ -31,7 +31,7 @@ available. So the stdlib modules using ``importc`` cannot be used with Nim's VM. However, at least the following modules are available: * `macros <macros.html>`_ -* `ospaths <ospaths.html>`_ +* `os <os.html>`_ * `strutils <strutils.html>`_ * `math <math.html>`_ * `distros <distros.html>`_ diff --git a/doc/tut2.rst b/doc/tut2.rst index 39e3bd89a..d0c6e7247 100644 --- a/doc/tut2.rst +++ b/doc/tut2.rst @@ -13,7 +13,6 @@ Introduction "Repetition renders the ridiculous reasonable." -- Norman Wildberger - This document is a tutorial for the advanced constructs of the *Nim* programming language. **Note that this document is somewhat obsolete as the** `manual <manual.html>`_ **contains many more examples of the advanced language @@ -652,369 +651,8 @@ avoid a common bug: to forget to close the file. Note how the ``let fn = filename`` statement ensures that ``filename`` is evaluated only once. -Macros -====== - -Macros enable advanced compile-time code transformations, but they cannot -change Nim's syntax. However, this is no real restriction because Nim's -syntax is flexible enough anyway. Macros have to be implemented in pure Nim -code if the `foreign function interface (FFI) -<manual.html#foreign-function-interface>`_ is not enabled in the compiler, but -other than that restriction (which at some point in the future will go away) -you can write any kind of Nim code and the compiler will run it at compile -time. - -There are two ways to write a macro, either *generating* Nim source code and -letting the compiler parse it, or creating manually an abstract syntax tree -(AST) which you feed to the compiler. In order to build the AST one needs to -know how the Nim concrete syntax is converted to an abstract syntax tree -(AST). The AST is documented in the `macros <macros.html>`_ module. - -Once your macro is finished, there are two ways to invoke it: -(1) invoking a macro like a procedure call (expression macros) -(2) invoking a macro with the special ``macrostmt`` - syntax (statement macros) - - -Expression Macros ------------------ - -The following example implements a powerful ``debug`` command that accepts a -variable number of arguments: - -.. code-block:: nim - :test: "nim c $1" - # to work with Nim syntax trees, we need an API that is defined in the - # ``macros`` module: - import macros - - macro debug(n: varargs[untyped]): typed = - # `n` is a Nim AST that contains a list of expressions; - # this macro returns a list of statements (n is passed for proper line - # information): - result = newNimNode(nnkStmtList, n) - # iterate over any argument that is passed to this macro: - for x in n: - # add a call to the statement list that writes the expression; - # `toStrLit` converts an AST to its string representation: - result.add(newCall("write", newIdentNode("stdout"), toStrLit(x))) - # add a call to the statement list that writes ": " - result.add(newCall("write", newIdentNode("stdout"), newStrLitNode(": "))) - # add a call to the statement list that writes the expressions value: - result.add(newCall("writeLine", newIdentNode("stdout"), x)) - - var - a: array[0..10, int] - x = "some string" - a[0] = 42 - a[1] = 45 - - debug(a[0], a[1], x) - -The macro call expands to: - -.. code-block:: nim - write(stdout, "a[0]") - write(stdout, ": ") - writeLine(stdout, a[0]) - - write(stdout, "a[1]") - write(stdout, ": ") - writeLine(stdout, a[1]) - - write(stdout, "x") - write(stdout, ": ") - writeLine(stdout, x) - - - -Statement Macros ----------------- - -Statement macros are defined just as expression macros. However, they are -invoked by an expression following a colon. - -The following example outlines a macro that generates a lexical analyzer from -regular expressions: - -.. code-block:: nim - - macro case_token(n: varargs[untyped]): typed = - # creates a lexical analyzer from regular expressions - # ... (implementation is an exercise for the reader :-) - discard - - case_token: # this colon tells the parser it is a macro statement - of r"[A-Za-z_]+[A-Za-z_0-9]*": - return tkIdentifier - of r"0-9+": - return tkInteger - of r"[\+\-\*\?]+": - return tkOperator - else: - return tkUnknown - - -Building your first macro -------------------------- - -To give a footstart to writing macros we will show now how to turn your typical -dynamic code into something that compiles statically. For the exercise we will -use the following snippet of code as the starting point: - -.. code-block:: nim - :test: "nim c $1" - - import strutils, tables - - proc readCfgAtRuntime(cfgFilename: string): Table[string, string] = - let - inputString = readFile(cfgFilename) - var - source = "" - - result = initTable[string, string]() - for line in inputString.splitLines: - # Ignore empty lines - if line.len < 1: continue - var chunks = split(line, ',') - if chunks.len != 2: - quit("Input needs comma split values, got: " & line) - result[chunks[0]] = chunks[1] - - if result.len < 1: quit("Input file empty!") - - let info = readCfgAtRuntime("data.cfg") - - when isMainModule: - echo info["licenseOwner"] - echo info["licenseKey"] - echo info["version"] - -Presumably this snippet of code could be used in a commercial software, reading -a configuration file to display information about the person who bought the -software. This external file would be generated by an online web shopping cart -to be included along the program containing the license information:: - - version,1.1 - licenseOwner,Hyori Lee - licenseKey,M1Tl3PjBWO2CC48m - -The ``readCfgAtRuntime`` proc will open the given filename and return a -``Table`` from the `tables module <tables.html>`_. The parsing of the file is -done (without much care for handling invalid data or corner cases) using the -`splitLines proc from the strutils module <strutils.html#splitLines>`_. There -are many things which can fail; mind the purpose is explaining how to make -this run at compile time, not how to properly implement a DRM scheme. - -The reimplementation of this code as a compile time proc will allow us to get -rid of the ``data.cfg`` file we would need to distribute along the binary, plus -if the information is really constant, it doesn't make from a logical point of -view to have it *mutable* in a global variable, it would be better if it was a -constant. Finally, and likely the most valuable feature, we can implement some -verification at compile time. You could think of this as a *better unit -testing*, since it is impossible to obtain a binary unless everything is -correct, preventing you to ship to users a broken program which won't start -because a small critical file is missing or its contents changed by mistake to -something invalid. - - -Generating source code -++++++++++++++++++++++ - -Our first attempt will start by modifying the program to generate a compile -time string with the *generated source code*, which we then pass to the -``parseStmt`` proc from the `macros module <macros.html>`_. Here is the -modified source code implementing the macro: - -.. code-block:: nim - :number-lines: - - import macros, strutils - - macro readCfgAndBuildSource(cfgFilename: string): typed = - let - inputString = slurp(cfgFilename.strVal) - var - source = "" - - for line in inputString.splitLines: - # Ignore empty lines - if line.len < 1: continue - var chunks = split(line, ',') - if chunks.len != 2: - error("Input needs comma split values, got: " & line) - source &= "const cfg" & chunks[0] & "= \"" & chunks[1] & "\"\n" - - if source.len < 1: error("Input file empty!") - result = parseStmt(source) - - readCfgAndBuildSource("data.cfg") - - when isMainModule: - echo cfglicenseOwner - echo cfglicenseKey - echo cfgversion - -The good news is not much has changed! First, we need to change the handling -of the input parameter (line 3). In the dynamic version the -``readCfgAtRuntime`` proc receives a string parameter. However, in the macro -version it is also declared as string, but this is the *outside* interface of -the macro. When the macro is run, it actually gets a ``PNimNode`` object -instead of a string, and we have to call the `strVal proc -<macros.html#strVal>`_ (line 5) from the `macros module <macros.html>`_ to -obtain the string being passed in to the macro. - -Second, we cannot use the `readFile proc <system.html#readFile>`_ from the -`system module <system.html>`_ due to FFI restriction at compile time. If we -try to use this proc, or any other which depends on FFI, the compiler will -error with the message ``cannot evaluate`` and a dump of the macro's source -code, along with a stack trace where the compiler reached before bailing out. -We can get around this limitation by using the `slurp proc -<system.html#slurp>`_ from the `system module <system.html>`_, which was -precisely made for compilation time (just like `gorge <system.html#gorge>`_ -which executes an external program and captures its output). - -The interesting thing is that our macro does not return a runtime `Table -<tables.html#Table>`_ object. Instead, it builds up Nim source code into -the ``source`` variable. For each line of the configuration file a ``const`` -variable will be generated (line 15). To avoid conflicts we prefix these -variables with ``cfg``. In essence, what the compiler is doing is replacing -the line calling the macro with the following snippet of code: - -.. code-block:: nim - const cfgversion = "1.1" - const cfglicenseOwner = "Hyori Lee" - const cfglicenseKey = "M1Tl3PjBWO2CC48m" - -You can verify this yourself adding the line ``echo source`` somewhere at the -end of the macro and compiling the program. Another difference is that instead -of calling the usual `quit proc <system.html#quit>`_ to abort (which we could -still call) this version calls the `error proc <macros.html#error>`_ (line -14). The ``error`` proc has the same behavior as ``quit`` but will dump also -the source and file line information where the error happened, making it -easier for the programmer to find where compilation failed. In this situation -it would point to the line invoking the macro, but **not** the line of -``data.cfg`` we are processing, that's something the macro itself would need -to control. - - -Generating AST by hand -++++++++++++++++++++++ - -To generate an AST we would need to intimately know the structures used by the -Nim compiler exposed in the `macros module <macros.html>`_, which at first -look seems a daunting task. But we can use as helper shortcut the `dumpTree -macro <macros.html#dumpTree>`_, which is used as a statement macro instead of -an expression macro. Since we know that we want to generate a bunch of -``const`` symbols we can create the following source file and compile it to -see what the compiler *expects* from us: - -.. code-block:: nim - :test: "nim c $1" - import macros - - dumpTree: - const cfgversion: string = "1.1" - const cfglicenseOwner = "Hyori Lee" - const cfglicenseKey = "M1Tl3PjBWO2CC48m" - -During compilation of the source code we should see the following lines in the -output (again, since this is a macro, compilation is enough, you don't have to -run any binary):: - - StmtList - ConstSection - ConstDef - Ident !"cfgversion" - Ident !"string" - StrLit 1.1 - ConstSection - ConstDef - Ident !"cfglicenseOwner" - Empty - StrLit Hyori Lee - ConstSection - ConstDef - Ident !"cfglicenseKey" - Empty - StrLit M1Tl3PjBWO2CC48m - -With this output we have a better idea of what kind of input the compiler -expects. We need to generate a list of statements. For each constant the source -code generates a ``ConstSection`` and a ``ConstDef``. If we were to move all -the constants to a single ``const`` block we would see only a single -``ConstSection`` with three children. - -Maybe you didn't notice, but in the ``dumpTree`` example the first constant -explicitly specifies the type of the constant. That's why in the tree output -the two last constants have their second child ``Empty`` but the first has a -string identifier. So basically a ``const`` definition is made up from an -identifier, optionally a type (can be an *empty* node) and the value. Armed -with this knowledge, let's look at the finished version of the AST building -macro: - -.. code-block:: nim - :number-lines: - - import macros, strutils - - macro readCfgAndBuildAST(cfgFilename: string): typed = - let - inputString = slurp(cfgFilename.strVal) - - result = newNimNode(nnkStmtList) - for line in inputString.splitLines: - # Ignore empty lines - if line.len < 1: continue - var chunks = split(line, ',') - if chunks.len != 2: - error("Input needs comma split values, got: " & line) - var - section = newNimNode(nnkConstSection) - constDef = newNimNode(nnkConstDef) - constDef.add(newIdentNode("cfg" & chunks[0])) - constDef.add(newEmptyNode()) - constDef.add(newStrLitNode(chunks[1])) - section.add(constDef) - result.add(section) - - if result.len < 1: error("Input file empty!") - - readCfgAndBuildAST("data.cfg") - - when isMainModule: - echo cfglicenseOwner - echo cfglicenseKey - echo cfgversion - -Since we are building on the previous example generating source code, we will -only mention the differences to it. Instead of creating a temporary ``string`` -variable and writing into it source code as if it were written *by hand*, we -use the ``result`` variable directly and create a statement list node -(``nnkStmtList``) which will hold our children (line 7). - -For each input line we have to create a constant definition (``nnkConstDef``) -and wrap it inside a constant section (``nnkConstSection``). Once these -variables are created, we fill them hierarchichally (line 17) like the -previous AST dump tree showed: the constant definition is a child of the -section definition, and the constant definition has an identifier node, an -empty node (we let the compiler figure out the type), and a string literal -with the value. - -A last tip when writing a macro: if you are not sure the AST you are building -looks ok, you may be tempted to use the ``dumpTree`` macro. But you can't use -it *inside* the macro you are writting/debugging. Instead ``echo`` the string -generated by `treeRepr <macros.html#treeRepr>`_. If at the end of the this -example you add ``echo treeRepr(result)`` you should get the same output as -using the ``dumpTree`` macro, but of course you can call that at any point of -the macro where you might be having troubles. - -Example Templates and Macros -============================ - -Lifting Procs -+++++++++++++ +Example: Lifting Procs +---------------------- .. code-block:: nim :test: "nim c $1" @@ -1039,36 +677,6 @@ Lifting Procs liftScalarProc(sqrt) # make sqrt() work for sequences echo sqrt(@[4.0, 16.0, 25.0, 36.0]) # => @[2.0, 4.0, 5.0, 6.0] -Identifier Mangling -+++++++++++++++++++ - -.. code-block:: nim - proc echoHW() = - echo "Hello world" - proc echoHW0() = - echo "Hello world 0" - proc echoHW1() = - echo "Hello world 1" - - template joinSymbols(a, b: untyped): untyped = - `a b`() - - joinSymbols(echo, HW) - - macro str2Call(s1, s2): typed = - result = newNimNode(nnkStmtList) - for i in 0..1: - # combines s1, s2 and an integer into an proc identifier - # that is called in a statement list - result.add(newCall(!($s1 & $s2 & $i))) - - str2Call("echo", "HW") - - # Output: - # Hello world - # Hello world 0 - # Hello world 1 - Compilation to JavaScript ========================= @@ -1083,3 +691,9 @@ JavaScript-compatible code you should remember the following: - ``cstring`` in JavaScript means JavaScript string. It is a good practice to use ``cstring`` only when it is semantically appropriate. E.g. don't use ``cstring`` as a binary data buffer. + + +Part 3 +====== + +Next part will be entirely about metaprogramming via macros: `Part III <tut3.html>`_ diff --git a/doc/tut3.rst b/doc/tut3.rst new file mode 100644 index 000000000..5590db8fe --- /dev/null +++ b/doc/tut3.rst @@ -0,0 +1,354 @@ +======================= +Nim Tutorial (Part III) +======================= + +:Author: Arne Döring +:Version: |nimversion| + +.. contents:: + + +Introduction +============ + + "With Great Power Comes Great Responsibility." -- Spider Man's Uncle + +This document is a tutorial about Nim's macro system. +A macro is a function that is executed at compile time and transforms +a Nim syntax tree into a different tree. + +Examples of things that can be implemented in macros: + + * An assert macro that prints both sides of a comparison operator, if +the assertion fails. ``myAssert(a == b)`` is converted to +``if a != b: quit($a " != " $b)`` + + * A debug macro that prints the value and the name of the symbol. +``myDebugEcho(a)`` is converted to ``echo "a: ", a`` + + * Symbolic differentiation of an expression. +``diff(a*pow(x,3) + b*pow(x,2) + c*x + d, x)`` is converted to +``3*a*pow(x,2) + 2*a*x + c`` + + +Macro Arguments +--------------- + +The types of macro arguments have two faces. One face is used for +the overload resolution, and the other face is used within the macro +body. For example, if ``macro foo(arg: int)`` is called in an +expression ``foo(x)``, ``x`` has to be of a type compatible to int, but +*within* the macro's body ``arg`` has the type ``NimNode``, not ``int``! +Why it is done this way will become obvious later, when we have seen +concrete examples. + +There are two ways to pass arguments to a macro, an argument can be +either ``typed`` or ``untyped``. + + +Untyped Arguments +----------------- + +Untyped macro arguments are passed to the macro before they are +semantically checked. This means the syntax tree that is passed down +to the macro does not need to make sense for Nim yet, the only +limitation is that it needs to be parseable. Usually the macro does +not check the argument either but uses it in the transformation's +result somehow. The result of a macro expansion is always checked +by the compiler, so apart from weird error messages nothing bad +can happen. + +The downside for an ``untyped`` argument is that these do not play +well with Nim's overloading resolution. + +The upside for untyped arguments is that the syntax tree is +quite predictable and less complex compared to its ``typed`` +counterpart. + + +Typed Arguments +--------------- + +For typed arguments, the semantic checker runs on the argument and +does transformations on it, before it is passed to the macro. Here +identifier nodes are resolved as symbols, implicit type +conversions are visible in the tree as calls, templates are +expanded and probably most importantly, nodes have type information. +Typed arguments can have the type ``typed`` in the arguments list. +But all other types, such as ``int``, ``float`` or ``MyObjectType`` +are typed arguments as well, and they are passed to the macro as a +syntax tree. + + +Static Arguments +---------------- + +Static arguments are a way to pass values as values and not as syntax +tree nodes to a macro. For example for ``macro foo(arg: static[int])`` +in the expression ``foo(x)``, ``x`` needs to be an integer constant, +but in the macro body ``arg`` is just like a normal parameter of type +``int``. + +.. code-block:: nim + + import macros + + macro myMacro(arg: static[int]): untyped = + echo arg # just an int (7), not ``NimNode`` + + myMacro(1 + 2 * 3) + + +Code blocks as arguments +------------------------ + +It is possible to pass the last argument of a call expression in a +separate code block with indentation. For example the following code +example is a valid (but not a recommended) way to call ``echo``: + +.. code-block:: nim + + echo "Hello ": + let a = "Wor" + let b = "ld!" + a & b + +For macros this way of calling is very useful; syntax trees of arbitrary +complexity can be passed to macros with this notation. + + +The Syntax Tree +--------------- + +In order to build a Nim syntax tree one needs to know how Nim source +code is represented as a syntax tree, and how such a tree needs to +look like so that the Nim compiler will understand it. The nodes of the +Nim syntax tree are documented in the `macros <macros.html>`_ module. +But a more interactive way to explore the Nim +syntax tree is with ``macros.treeRepr``, it converts a syntax tree +into a multi line string for printing on the console. It can be used +to explore how the argument expressions are represented in tree form +and for debug printing of generated syntax tree. ``dumpTree`` is a +predefined macro that just prints its argument in tree representation, +but does nothing else. Here is an example of such a tree representation: + +.. code-block:: nim + + dumpTree: + var mt: MyType = MyType(a:123.456, b:"abcdef") + + # output: + # StmtList + # VarSection + # IdentDefs + # Ident "mt" + # Ident "MyType" + # ObjConstr + # Ident "MyType" + # ExprColonExpr + # Ident "a" + # FloatLit 123.456 + # ExprColonExpr + # Ident "b" + # StrLit "abcdef" + + +Custom sematic checking +----------------------- + +The first thing that a macro should do with its arguments is to check +if the argument is in the correct form. Not every type of wrong input +needs to be caught here, but anything that could cause a crash during +macro evaluation should be caught and create a nice error message. +``macros.expectKind`` and ``macros.expectLen`` are a good start. If +the checks need to be more complex, arbitrary error messages can +be created with the ``macros.error`` proc. + +.. code-block:: nim + + macro myAssert(arg: untyped): untyped = + arg.expectKind nnkInfix + + +Generating Code +--------------- + +There are two ways to generate the code. Either by creating the syntax +tree with expressions that contain a lot of calls to ``newTree`` and +``newLit``, or with ``quote do:`` expressions. The first option offers +the best low level control for the syntax tree generation, but the +second option is much less verbose. If you choose to create the syntax +tree with calls to ``newTree`` and ``newLit`` the macro +``marcos.dumpAstGen`` can help you with the verbosity. ``quote do:`` +allows you to write the code that you want to generate literally, +backticks are used to insert code from ``NimNode`` symbols into the +generated expression. This means that you can't use backticks within +``quote do:`` for anything else than injecting symbols. Make sure to +inject only symbols of type ``NimNode`` into the generated syntax +tree. You can use ``newLit`` to convert arbitrary values into +expressions trees of type ``NimNode`` so that it is safe to inject +them into the tree. + + +.. code-block:: nim + :test: "nim c $1" + + import macros + + type + MyType = object + a: float + b: string + + macro myMacro(arg: untyped): untyped = + var mt: MyType = MyType(a:123.456, b:"abcdef") + + # ... + + let mtLit = newLit(mt) + + result = quote do: + echo `arg` + echo `mtLit` + + myMacro("Hallo") + +The call to ``myMacro`` will generate the following code: + +.. code-block:: nim + echo "Hallo" + echo MyType(a: 123.456'f64, b: "abcdef") + + +Building your first macro +------------------------- + +To give a footstart to writing macros we will show now how to +implement the ``myDebug`` macro mentioned earlier. The first thing to +do is to build a simple example of the macro usage, and then just +print the argument. This way it is possible to get an idea of a +correct argument should be look like. + +.. code-block:: nim + :test: "nim c $1" + + import macros + + macro myAssert(arg: untyped): untyped = + echo arg.treeRepr + + let a = 1 + let b = 2 + + myAssert(a != b) + +.. code-block:: + + Infix + Ident "!=" + Ident "a" + Ident "b" + + +From the output it is possible to see that the information that the +argument is an infix operator (node kind is "Infix"), as well as that the two +operands are at index 1 and 2. With this information the actual +macro can be written. + +.. code-block:: nim + :test: "nim c $1" + + import macros + + macro myAssert(arg: untyped): untyped = + # all node kind identifiers are prefixed with "nnk" + arg.expectKind nnkInfix + arg.expectLen 3 + # operator as string literal + let op = newLit(" " & arg[0].repr & " ") + let lhs = arg[1] + let rhs = arg[2] + + result = quote do: + if not `arg`: + raise newException(AssertionError,$`lhs` & `op` & $`rhs`) + + let a = 1 + let b = 2 + + myAssert(a != b) + myAssert(a == b) + + +This is the code that will be generated. To debug what the macro +actually generated, the statement ``echo result.repr`` can be used, in +the last line of the macro. It is also the statement that has been +used to get this output. + +.. code-block:: nim + if not (a != b): + raise newException(AssertionError, $a & " != " & $b) + +With Power Comes Responsibility +------------------------------- + +Macros are very powerful. A good advice is to use them as little as +possible, but as much as necessary. Macros can change the semantics of +expressions, making the code incomprehensible for anybody who does not +know exactly what the macro does with it. So whenever a macro is not +necessary and the same logic can be implemented using templates or +generics, it is probably better not to use a macro. And when a macro +is used for something, the macro should better have a well written +documentation. For all the people who claim to write only perfectly +self-explanatory code: when it comes to macros, the implementation is +not enough for documentation. + +Limitations +----------- + +Since macros are evaluated in the compiler in the NimVM, macros share +all the limitations of the NimVM. They have to be implemented in pure Nim +code. Macros can start external processes on the shell, but they +cannot call C functions except from those that are built in the +compiler. + + +More Examples +============= + +This tutorial can only cover the basics of the macro system. There are +macros out there that could be an inspiration for you of what is +possible with it. + + +Strformat +--------- + +In the Nim standard library, the ``strformat`` library provides a +macro that parses a string literal at compile time. Parsing a string +in a macro like here is generally not recommended. The parsed AST +cannot have type information, and parsing implemented on the VM is +generally not very fast. Working on AST nodes is almost always the +recommended way. But still ``strformat`` is a good example for a +practical use case for a macro that is slightly more complex that the +``assert`` macro. + +`Strformat <https://github.com/nim-lang/Nim/blob/5845716df8c96157a047c2bd6bcdd795a7a2b9b1/lib/pure/strformat.nim#L280>`_ + +Ast Pattern Matching +-------------------- + +Ast Pattern Matching is a macro library to aid in writing complex +macros. This can be seen as a good example of how to repurpose the +Nim syntax tree with new semantics. + +`Ast Pattern Matching <https://github.com/krux02/ast-pattern-matching>`_ + +OpenGL Sandbox +-------------- + +This project has a working Nim to GLSL compiler written entirely in +macros. It scans recursively though all used function symbols to +compile them so that cross library functions can be executed on the GPU. + +`OpenGL Sandbox <https://github.com/krux02/opengl-sandbox>`_ diff --git a/examples/tunit.nim b/examples/tunit.nim index 785b9aa5e..bc447812d 100644 --- a/examples/tunit.nim +++ b/examples/tunit.nim @@ -1,3 +1,4 @@ + import unittest, macros @@ -44,4 +45,3 @@ test "arithmetic failure": expect(ArithmeticError, CatchableError): discard foo() - diff --git a/issue_template.md b/issue_template.md deleted file mode 100644 index 42e29eacd..000000000 --- a/issue_template.md +++ /dev/null @@ -1,14 +0,0 @@ -<!--- Summarize the Problem here, keep it simple. --> -<!--- Think about the title twice. --> - -## Example -<!--- This could be a source code block --> - -## Possible Solution/Expected Behavior -<!--- Without a solution the problem can't be fixed. --> - -## Additional Information -<!--- For Example: - A link to a project where the issue is relevant. - A link to a related issue or discussion. - --> diff --git a/koch.nim b/koch.nim index 8e84ac5fa..9a8d38a79 100644 --- a/koch.nim +++ b/koch.nim @@ -51,6 +51,9 @@ Boot options: -d:release produce a release version of the compiler -d:useLinenoise use the linenoise library for interactive mode (not needed on Windows) + -d:leanCompiler produce a compiler without JS codegen or + documentation generator in order to use less RAM + for bootstrapping Commands for core developers: docs [options] generates the full documentation @@ -76,6 +79,8 @@ template withDir(dir, body) = finally: setCurrentdir(old) +setCurrentDir(getAppDir()) + proc tryExec(cmd: string): bool = echo(cmd) result = execShellCmd(cmd) == 0 @@ -112,10 +117,11 @@ proc bundleNimbleSrc(latest: bool) = proc bundleNimbleExe(latest: bool) = bundleNimbleSrc(latest) - # now compile Nimble and copy it to $nim/bin for the installer.ini - # to pick it up: - nimexec("c -d:release --nilseqs:on dist/nimble/src/nimble.nim") - copyExe("dist/nimble/src/nimble".exe, "bin/nimble".exe) + # installer.ini expects it under $nim/bin + nimCompile("dist/nimble/src/nimble.nim", options = "-d:release --nilseqs:on") + +proc buildNimfind() = + nimCompile("tools/nimfind.nim", options = "-d:release") proc buildNimble(latest: bool) = # old installations created nim/nimblepkg/*.nim files. We remove these @@ -141,33 +147,28 @@ proc buildNimble(latest: bool) = else: exec("git checkout -f stable") exec("git pull") - nimexec("c --noNimblePath -p:compiler --nilseqs:on -d:release " & installDir / "src/nimble.nim") - copyExe(installDir / "src/nimble".exe, "bin/nimble".exe) + nimCompile(installDir / "src/nimble.nim", options = "--noNimblePath --nilseqs:on -d:release") -proc bundleNimsuggest(buildExe: bool) = - if buildExe: - nimexec("c --noNimblePath -d:release -p:compiler nimsuggest/nimsuggest.nim") - copyExe("nimsuggest/nimsuggest".exe, "bin/nimsuggest".exe) - removeFile("nimsuggest/nimsuggest".exe) +proc bundleNimsuggest() = + nimCompile("nimsuggest/nimsuggest.nim", options = "-d:release") proc buildVccTool() = - nimexec("c -o:bin/vccexe.exe tools/vccenv/vccexe") + nimCompile("tools/vccenv/vccexe.nim") proc bundleWinTools() = - nimexec("c tools/finish.nim") - copyExe("tools/finish".exe, "finish".exe) - removeFile("tools/finish".exe) + # TODO: consider building under `bin` instead of `.` + nimCompile("tools/finish.nim", outputDir = "") + buildVccTool() - nimexec("c -o:bin/nimgrab.exe -d:ssl tools/nimgrab.nim") - nimexec("c -o:bin/nimgrep.exe tools/nimgrep.nim") + nimCompile("tools/nimgrab.nim", options = "-d:ssl") + nimCompile("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") + nimCompile(r"tools\downloader.nim", options = r"--cc:vcc --app:gui -d:ssl --noNimblePath --path:..\ui") proc zip(latest: bool; args: string) = bundleNimbleExe(latest) - bundleNimsuggest(true) + bundleNimsuggest() bundleWinTools() nimexec("cc -r $2 --var:version=$1 --var:mingw=none --main:compiler/nim.nim scripts compiler/installer.ini" % [VersionAsString, compileNimInst]) @@ -184,7 +185,8 @@ proc ensureCleanGit() = proc xz(latest: bool; args: string) = ensureCleanGit() bundleNimbleSrc(latest) - bundleNimsuggest(false) + when false: + bundleNimsuggest() nimexec("cc -r $2 --var:version=$1 --var:mingw=none --main:compiler/nim.nim scripts compiler/installer.ini" % [VersionAsString, compileNimInst]) exec("$# --var:version=$# --var:mingw=none --main:compiler/nim.nim xz compiler/installer.ini" % @@ -195,19 +197,16 @@ proc buildTool(toolname, args: string) = copyFile(dest="bin" / splitFile(toolname).name.exe, source=toolname.exe) proc buildTools(latest: bool) = - nimexec "c --noNimblePath -p:compiler -d:release -o:" & ("bin/nimsuggest".exe) & - " nimsuggest/nimsuggest.nim" - - nimexec "c -d:release -o:" & ("bin/nimgrep".exe) & " tools/nimgrep.nim" + bundleNimsuggest() + nimCompile("tools/nimgrep.nim", options = "-d:release") when defined(windows): buildVccTool() - - nimexec "c -o:" & ("bin/nimpretty".exe) & " nimpretty/nimpretty.nim" - + nimCompile("nimpretty/nimpretty.nim", options = "-d:release") buildNimble(latest) + buildNimfind() proc nsis(latest: bool; args: string) = bundleNimbleExe(latest) - bundleNimsuggest(true) + bundleNimsuggest() bundleWinTools() # make sure we have generated the niminst executables: buildTool("tools/niminst/niminst", args) @@ -545,7 +544,7 @@ when isMainModule: of "nimble": if stable: buildNimble(false) else: buildNimble(existsDir(".git") or latest) - of "nimsuggest": bundleNimsuggest(buildExe=true) + of "nimsuggest": bundleNimsuggest() of "tools": if stable: buildTools(false) else: buildTools(existsDir(".git") or latest) diff --git a/lib/core/locks.nim b/lib/core/locks.nim index f9add0037..d6d579ba0 100644 --- a/lib/core/locks.nim +++ b/lib/core/locks.nim @@ -17,8 +17,6 @@ type ## or not is unspecified! Cond* = SysCond ## Nim condition variable -{.deprecated: [TLock: Lock, TCond: Cond].} - {.push stackTrace: off.} proc initLock*(lock: var Lock) {.inline.} = diff --git a/lib/core/macros.nim b/lib/core/macros.nim index a146117d0..f45ca3f82 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -168,6 +168,18 @@ proc `[]`*(n: NimNode, i: int): NimNode {.magic: "NChild", noSideEffect.} proc `[]`*(n: NimNode, i: BackwardsIndex): NimNode = n[n.len - i.int] ## get `n`'s `i`'th child. +template `^^`(n: NimNode, i: untyped): untyped = + (when i is BackwardsIndex: n.len - int(i) else: int(i)) + +proc `[]`*[T, U](n: NimNode, x: HSlice[T, U]): seq[NimNode] = + ## slice operation for NimNode. + ## returns a seq of child of `n` who inclusive range [n[x.a], n[x.b]]. + let xa = n ^^ x.a + let L = (n ^^ x.b) - xa + 1 + result = newSeq[NimNode](L) + for i in 0..<L: + result[i] = n[i + xa] + proc `[]=`*(n: NimNode, i: int, child: NimNode) {.magic: "NSetChild", noSideEffect.} ## set `n`'s `i`'th child to `child`. @@ -262,6 +274,12 @@ when defined(nimHasSymOwnerInMacro): ## result is also mnde of kind nnkSym if owner exists otherwise ## nnkNilLit is returned +when defined(nimHasInstantiationOfInMacro): + proc isInstantiationOf*(instanceProcSym, genProcSym: NimNode): bool {.magic: "SymIsInstantiationOf", noSideEffect.} + ## check if proc symbol is instance of the generic proc symbol + ## useful to check proc symbols against generic symbols + ## returned by `bindSym` + proc getType*(n: NimNode): NimNode {.magic: "NGetType", noSideEffect.} ## with 'getType' you can access the node's `type`:idx:. A Nim type is ## mapped to a Nim AST too, so it's slightly confusing but it means the same @@ -406,8 +424,6 @@ type ## if not ambiguous (this cannot be achieved with ## any other means in the language currently) -{.deprecated: [TBindSymRule: BindSymRule].} - proc bindSym*(ident: string | NimNode, rule: BindSymRule = brClosed): NimNode {. magic: "NBindSym", noSideEffect.} ## creates a node that binds `ident` to a symbol node. The bound symbol @@ -696,6 +712,16 @@ proc newLit*[T](arg: seq[T]): NimNode {.compileTime.} = ), bracket ) +proc newLit*(arg: enum): NimNode {.compileTime.} = + result = newCall( + arg.type.getTypeInst[1], + newLit(int(arg)) + ) + +proc newLit*[T](s: set[T]): NimNode {.compileTime.} = + result = nnkCurly.newTree + for x in s: + result.add newLit(x) proc newLit*(arg: tuple): NimNode {.compileTime.} = result = nnkPar.newTree diff --git a/lib/core/seqs.nim b/lib/core/seqs.nim index 4dcf6cbbb..fb81a30de 100644 --- a/lib/core/seqs.nim +++ b/lib/core/seqs.nim @@ -8,9 +8,11 @@ # -import typetraits +# import typetraits # strs already imported allocators for us. +proc supportsCopyMem(t: typedesc): bool {.magic: "TypeTrait".} + ## Default seq implementation used by Nim's core. type NimSeqPayload {.core.}[T] = object @@ -123,7 +125,7 @@ proc grow*[T](x: var seq[T]; newLen: Natural; value: T) = if newLen <= oldLen: return var xu = cast[ptr NimSeqV2[T]](addr x) - xu.p = prepareSeqAdd(oldLen, xu.p, newLen - oldLen, sizeof(T)) + xu.p = cast[typeof(xu.p)](prepareSeqAdd(oldLen, xu.p, newLen - oldLen, sizeof(T))) xu.len = newLen for i in oldLen .. newLen-1: x.data[i] = value diff --git a/lib/core/typeinfo.nim b/lib/core/typeinfo.nim index c78752360..14613c50d 100644 --- a/lib/core/typeinfo.nim +++ b/lib/core/typeinfo.nim @@ -83,8 +83,6 @@ when not defined(js): template `rawType=`(x: var Any, p: PNimType) = x.rawTypePtr = cast[pointer](p) -{.deprecated: [TAny: Any, TAnyKind: AnyKind].} - when defined(gogc): const GenericSeqSize = (3 * sizeof(int)) else: diff --git a/lib/deprecated/pure/actors.nim b/lib/deprecated/pure/actors.nim index 451668825..77c67a3e4 100644 --- a/lib/deprecated/pure/actors.nim +++ b/lib/deprecated/pure/actors.nim @@ -111,7 +111,6 @@ type actors: seq[PActor[In, Out]] when Out isnot void: outputs: Channel[Out] -{.deprecated: [TActorPool: ActorPool].} proc `^`*[T](f: ptr Channel[T]): T = ## alias for 'recv'. diff --git a/lib/deprecated/pure/asyncio.nim b/lib/deprecated/pure/asyncio.nim index 39d77230a..2dd08024d 100644 --- a/lib/deprecated/pure/asyncio.nim +++ b/lib/deprecated/pure/asyncio.nim @@ -147,11 +147,6 @@ type SockIdle, SockConnecting, SockConnected, SockListening, SockClosed, SockUDPBound -{.deprecated: [TDelegate: DelegateObj, PDelegate: Delegate, - TInfo: SocketStatus, PAsyncSocket: AsyncSocket, TAsyncSocket: AsyncSocketObj, - TDispatcher: DispatcherObj, PDispatcher: Dispatcher, - ].} - proc newDelegate*(): Delegate = ## Creates a new delegate. diff --git a/lib/deprecated/pure/ftpclient.nim b/lib/deprecated/pure/ftpclient.nim index 7645258b6..d70f9556a 100644 --- a/lib/deprecated/pure/ftpclient.nim +++ b/lib/deprecated/pure/ftpclient.nim @@ -108,11 +108,6 @@ type ReplyError* = object of IOError FTPError* = object of IOError -{.deprecated: [ - TFTPClient: FTPClientObj, TFTPJob: FTPJob, PAsyncFTPClient: AsyncFTPClient, - TAsyncFTPClient: AsyncFTPClientObj, TFTPEvent: FTPEvent, - EInvalidReply: ReplyError, EFTP: FTPError -].} const multiLineLimit = 10000 diff --git a/lib/deprecated/pure/ospaths.nim b/lib/deprecated/pure/ospaths.nim new file mode 100644 index 000000000..34d452fd2 --- /dev/null +++ b/lib/deprecated/pure/ospaths.nim @@ -0,0 +1,21 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2015 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## This module is deprecated, ``import os`` instead. +{.deprecated: "import os.nim instead".} + +import os +export ReadEnvEffect, WriteEnvEffect, ReadDirEffect, WriteDirEffect, OSErrorCode, + doslikeFileSystem, CurDir, ParDir, DirSep, AltSep, PathSep, FileSystemCaseSensitive, + ExeExt, ScriptExt, DynlibFormat, ExtSep, joinPath, `/`, splitPath, parentDir, + tailDir, isRootDir, parentDirs, `/../`, searchExtPos, splitFile, extractFilename, + lastPathPart, changeFileExt, addFileExt, cmpPaths, isAbsolute, unixToNativePath, + `==`, `$`, osErrorMsg, raiseOSError, osLastError, getEnv, existsEnv, putEnv, + getHomeDir, getConfigDir, getTempDir, expandTilde, quoteShellWindows, + quoteShellPosix, quoteShell, quoteShellCommand diff --git a/lib/deprecated/pure/parseurl.nim b/lib/deprecated/pure/parseurl.nim index 6d58e8a73..b19f6dc85 100644 --- a/lib/deprecated/pure/parseurl.nim +++ b/lib/deprecated/pure/parseurl.nim @@ -22,8 +22,6 @@ type scheme, username, password, hostname, port, path, query, anchor: string] -{.deprecated: [TUrl: Url].} - proc parseUrl*(url: string): Url {.deprecated.} = var i = 0 diff --git a/lib/deprecated/pure/sockets.nim b/lib/deprecated/pure/sockets.nim index 34881607f..f78df0b2b 100644 --- a/lib/deprecated/pure/sockets.nim +++ b/lib/deprecated/pure/sockets.nim @@ -70,9 +70,6 @@ when defined(ssl): SSLAcceptResult* = enum AcceptNoClient = 0, AcceptNoHandshake, AcceptSuccess - {.deprecated: [ESSL: SSLError, TSSLCVerifyMode: SSLCVerifyMode, - TSSLProtVersion: SSLProtVersion, PSSLContext: SSLContext, - TSSLAcceptResult: SSLAcceptResult].} const BufferSize*: int = 4000 ## size of a buffered socket's buffer @@ -147,12 +144,6 @@ type TimeoutError* = object of Exception -{.deprecated: [TSocket: Socket, TType: SockType, TPort: Port, TDomain: Domain, - TProtocol: Protocol, TServent: Servent, THostent: Hostent, - TSOBool: SOBool, TRecvLineResult: RecvLineResult, - TReadLineResult: ReadLineResult, ETimeout: TimeoutError, - TSocketImpl: SocketImpl].} - when defined(booting): let invalidSocket*: Socket = nil ## invalid socket else: diff --git a/lib/impure/db_mysql.nim b/lib/impure/db_mysql.nim index 26bc7d0ad..2fcb56202 100644 --- a/lib/impure/db_mysql.nim +++ b/lib/impure/db_mysql.nim @@ -96,7 +96,6 @@ type ## column text on demand row: cstringArray len: int -{.deprecated: [TRow: Row, TDbConn: DbConn].} proc dbError*(db: DbConn) {.noreturn.} = ## raises a DbError exception. diff --git a/lib/impure/db_odbc.nim b/lib/impure/db_odbc.nim index 224bcbb50..72d7aa800 100644 --- a/lib/impure/db_odbc.nim +++ b/lib/impure/db_odbc.nim @@ -101,8 +101,6 @@ type ## used to get a row's ## column text on demand -{.deprecated: [TRow: Row, TSqlQuery: SqlQuery, TDbConn: DbConn].} - var buf: array[0..4096, char] diff --git a/lib/impure/db_postgres.nim b/lib/impure/db_postgres.nim index e765cc197..800673ca0 100644 --- a/lib/impure/db_postgres.nim +++ b/lib/impure/db_postgres.nim @@ -76,7 +76,6 @@ type res: PPGresult ## used to get a row's line: int ## column text on demand SqlPrepared* = distinct string ## a identifier for the prepared queries -{.deprecated: [TRow: Row, TDbConn: DbConn, TSqlPrepared: SqlPrepared].} proc dbError*(db: DbConn) {.noreturn.} = ## raises a DbError exception. diff --git a/lib/impure/db_sqlite.nim b/lib/impure/db_sqlite.nim index 52a168096..c7e373098 100644 --- a/lib/impure/db_sqlite.nim +++ b/lib/impure/db_sqlite.nim @@ -94,7 +94,6 @@ type ## converted to nil. InstantRow* = Pstmt ## a handle that can be used to get a row's column ## text on demand -{.deprecated: [TRow: Row, TDbConn: DbConn].} proc dbError*(db: DbConn) {.noreturn.} = ## raises a DbError exception. diff --git a/lib/impure/nre.nim b/lib/impure/nre.nim index 985913a88..58594f054 100644 --- a/lib/impure/nre.nim +++ b/lib/impure/nre.nim @@ -23,6 +23,12 @@ export options ## ## A regular expression library for Nim using PCRE to do the hard work. ## +## For documentation on how to write patterns, there exists `the official PCRE +## pattern documentation +## <https://www.pcre.org/original/doc/html/pcrepattern.html>`_. You can also +## search the internet for a wide variety of third-party documentation and +## tools. +## ## **Note**: If you love ``sequtils.toSeq`` we have bad news for you. This ## library doesn't work with it due to documented compiler limitations. As ## a workaround, use this: @@ -70,7 +76,10 @@ type ## comment".`` ## ## ``pattern: string`` - ## the string that was used to create the pattern. + ## the string that was used to create the pattern. For details on how + ## to write a pattern, please see `the official PCRE pattern + ## documentation. + ## <https://www.pcre.org/original/doc/html/pcrepattern.html>`_ ## ## ``captureCount: int`` ## the number of captures that the pattern has. @@ -126,6 +135,12 @@ type ## Convention <http://man7.org/linux/man-pages/man3/pcresyntax.3.html#NEWLINE_CONVENTION>`_ ## sections of the `PCRE syntax ## manual <http://man7.org/linux/man-pages/man3/pcresyntax.3.html>`_. + ## + ## Some of these options are not part of PCRE and are converted by nre + ## into PCRE flags. These include ``NEVER_UTF``, ``ANCHORED``, + ## ``DOLLAR_ENDONLY``, ``FIRSTLINE``, ``NO_AUTO_CAPTURE``, + ## ``JAVASCRIPT_COMPAT``, ``U``, ``NO_STUDY``. In other PCRE wrappers, you + ## will need to pass these as seperate flags to PCRE. pattern*: string ## not nil pcreObj: ptr pcre.Pcre ## not nil pcreExtra: ptr pcre.ExtraData ## nil diff --git a/lib/impure/re.nim b/lib/impure/re.nim index b142f58cd..dc4ee326f 100644 --- a/lib/impure/re.nim +++ b/lib/impure/re.nim @@ -547,7 +547,7 @@ proc split*(s: string, sep: Regex, maxsplit = -1): seq[string] {.inline.} = ## ## The portion matched by ``sep`` is not returned. result = @[] - for x in split(s, sep): result.add x + for x in split(s, sep, maxsplit): result.add x proc escapeRe*(s: string): string = ## escapes ``s`` so that it is matched verbatim when used as a regular @@ -636,6 +636,11 @@ when isMainModule: doAssert(accum == @["", "this", "is", "an", "example", ""]) accum = @[] + for word in split("00232this02939is39an22example111", re"\d+", maxsplit=2): + accum.add(word) + doAssert(accum == @["", "this", "is39an22example111"]) + + accum = @[] for word in split("AAA : : BBB", re"\s*:\s*"): accum.add(word) doAssert(accum == @["AAA", "", "BBB"]) @@ -647,6 +652,8 @@ when isMainModule: doAssert(split(";a;b;c", re";") == @["", "a", "b", "c"]) doAssert(split(";a;b;c;", re";") == @["", "a", "b", "c", ""]) doAssert(split("a;b;c;", re";") == @["a", "b", "c", ""]) + doAssert(split("00232this02939is39an22example111", re"\d+", maxsplit=2) == @["", "this", "is39an22example111"]) + for x in findAll("abcdef", re"^{.}", 3): doAssert x == "d" diff --git a/lib/impure/ssl.nim b/lib/impure/ssl.nim index 5b0e899f6..0bcb3f217 100644 --- a/lib/impure/ssl.nim +++ b/lib/impure/ssl.nim @@ -21,7 +21,6 @@ type SecureSocket* = object ssl: SslPtr bio: BIO -{.deprecated: [TSecureSocket: SecureSocket].} proc connect*(sock: var SecureSocket, address: string, port: int): int = diff --git a/lib/js/jsffi.nim b/lib/js/jsffi.nim index 307fe2382..e1c59803d 100644 --- a/lib/js/jsffi.nim +++ b/lib/js/jsffi.nim @@ -69,10 +69,25 @@ template mangleJsName(name: cstring): cstring = inc nameCounter "mangledName" & $nameCounter +# only values that can be mapped 1 to 1 with cstring should be keys: they have an injective function with cstring + +proc toJsKey*[T: SomeInteger](text: cstring, t: type T): T {.importcpp: "parseInt(#)".} + +proc toJsKey*[T: enum](text: cstring, t: type T): T = + T(text.toJsKey(int)) + +proc toJsKey*(text: cstring, t: type cstring): cstring = + text + +proc toJsKey*[T: SomeFloat](text: cstring, t: type T): T {.importcpp: "parseFloat(#)".} + type + JsKey* = concept a, type T + cstring.toJsKey(T) is type(a) + JsObject* = ref object of JsRoot ## Dynamically typed wrapper around a JavaScript object. - JsAssoc*[K, V] = ref object of JsRoot + JsAssoc*[K: JsKey, V] = ref object of JsRoot ## Statically typed wrapper around a JavaScript object. js* = JsObject @@ -104,7 +119,7 @@ type proc newJsObject*: JsObject {. importcpp: "{@}" .} ## Creates a new empty JsObject -proc newJsAssoc*[K, V]: JsAssoc[K, V] {. importcpp: "{@}" .} +proc newJsAssoc*[K: JsKey, V]: JsAssoc[K, V] {. importcpp: "{@}" .} ## Creates a new empty JsAssoc with key type `K` and value type `V`. # Checks @@ -176,21 +191,19 @@ proc `[]=`*[T](obj: JsObject, field: cstring, val: T) {. importcpp: setImpl .} proc `[]=`*[T](obj: JsObject, field: int, val: T) {. importcpp: setImpl .} ## Set the value of a property of name `field` in a JsObject `obj` to `v`. -proc `[]`*[K: not string, V](obj: JsAssoc[K, V], field: K): V - {. importcpp: getImpl .} - ## Return the value of a property of name `field` from a JsAssoc `obj`. - -proc `[]`*[V](obj: JsAssoc[string, V], field: cstring): V +proc `[]`*[K: JsKey, V](obj: JsAssoc[K, V], field: K): V {. importcpp: getImpl .} ## Return the value of a property of name `field` from a JsAssoc `obj`. -proc `[]=`*[K: not string, V](obj: JsAssoc[K, V], field: K, val: V) +proc `[]=`*[K: JsKey, V](obj: JsAssoc[K, V], field: K, val: V) {. importcpp: setImpl .} ## Set the value of a property of name `field` in a JsAssoc `obj` to `v`. -proc `[]=`*[V](obj: JsAssoc[string, V], field: cstring, val: V) - {. importcpp: setImpl .} - ## Set the value of a property of name `field` in a JsAssoc `obj` to `v`. +proc `[]`*[V](obj: JsAssoc[cstring, V], field: string): V = + obj[cstring(field)] + +proc `[]=`*[V](obj: JsAssoc[cstring, V], field: string, val: V) = + obj[cstring(field)] = val proc `==`*(x, y: JsRoot): bool {. importcpp: "(# === #)" .} ## Compare two JsObjects or JsAssocs. Be careful though, as this is comparison @@ -277,7 +290,7 @@ macro `.()`*(obj: JsObject, result[0][3].add newIdentDefs(paramName, newIdentNode(!"JsObject")) result[1].add args[idx].copyNimTree -macro `.`*[K: string | cstring, V](obj: JsAssoc[K, V], +macro `.`*[K: cstring, V](obj: JsAssoc[K, V], field: untyped): V = ## Experimental dot accessor (get) for type JsAssoc. ## Returns the value of a property of name `field` from a JsObject `x`. @@ -293,7 +306,7 @@ macro `.`*[K: string | cstring, V](obj: JsAssoc[K, V], {. importcpp: `importString`, gensym .} helper(`obj`) -macro `.=`*[K: string | cstring, V](obj: JsAssoc[K, V], +macro `.=`*[K: cstring, V](obj: JsAssoc[K, V], field: untyped, value: V): untyped = ## Experimental dot accessor (set) for type JsAssoc. @@ -310,7 +323,7 @@ macro `.=`*[K: string | cstring, V](obj: JsAssoc[K, V], {. importcpp: `importString`, gensym .} helper(`obj`, `value`) -macro `.()`*[K: string | cstring, V: proc](obj: JsAssoc[K, V], +macro `.()`*[K: cstring, V: proc](obj: JsAssoc[K, V], field: untyped, args: varargs[untyped]): auto = ## Experimental "method call" operator for type JsAssoc. @@ -354,24 +367,18 @@ iterator keys*(obj: JsObject): cstring = yield k {.emit: "}".} -iterator pairs*[K, V](assoc: JsAssoc[K, V]): (K,V) = +iterator pairs*[K: JsKey, V](assoc: JsAssoc[K, V]): (K,V) = ## Yields tuples of type ``(K, V)``, with the first entry ## being a `key` in the JsAssoc and the second being its corresponding value. - when K is string: - var k: cstring - else: - var k: K + var k: cstring var v: V {.emit: "for (var `k` in `assoc`) {".} {.emit: " if (!`assoc`.hasOwnProperty(`k`)) continue;".} {.emit: " `v`=`assoc`[`k`];".} - when K is string: - yield ($k, v) - else: - yield (k, v) + yield (k.toJsKey(K), v) {.emit: "}".} -iterator items*[K,V](assoc: JSAssoc[K,V]): V = +iterator items*[K, V](assoc: JSAssoc[K, V]): V = ## Yields the `values` in a JsAssoc. var v: V {.emit: "for (var k in `assoc`) {".} @@ -380,18 +387,12 @@ iterator items*[K,V](assoc: JSAssoc[K,V]): V = yield v {.emit: "}".} -iterator keys*[K,V](assoc: JSAssoc[K,V]): K = +iterator keys*[K: JsKey, V](assoc: JSAssoc[K, V]): K = ## Yields the `keys` in a JsAssoc. - when K is string: - var k: cstring - else: - var k: K + var k: cstring {.emit: "for (var `k` in `assoc`) {".} {.emit: " if (!`assoc`.hasOwnProperty(`k`)) continue;".} - when K is string: - yield $k - else: - yield k + yield k.toJsKey(K) {.emit: "}".} # Literal generation diff --git a/lib/nimrtl.nim b/lib/nimrtl.nim index 4e4cf7e0e..335207f54 100644 --- a/lib/nimrtl.nim +++ b/lib/nimrtl.nim @@ -33,4 +33,3 @@ when not defined(createNimRtl): import parseutils, strutils, parseopt, parsecfg, strtabs, unicode, pegs, ropes, os, osproc, times - diff --git a/lib/posix/inotify.nim b/lib/posix/inotify.nim index 359e88617..f88e48a2d 100644 --- a/lib/posix/inotify.nim +++ b/lib/posix/inotify.nim @@ -19,7 +19,6 @@ type cookie*{.importc: "cookie".}: uint32 # Cookie to synchronize two events. len*{.importc: "len".}: uint32 # Length (including NULs) of name. name*{.importc: "name".}: char # Name. -{.deprecated: [Tinotify_event: InotifyEvent].} # Supported events suitable for MASK parameter of INOTIFY_ADD_WATCH. const diff --git a/lib/posix/posix.nim b/lib/posix/posix.nim index fa589e905..0c66aa2b9 100644 --- a/lib/posix/posix.nim +++ b/lib/posix/posix.nim @@ -100,7 +100,6 @@ else: include posix_other # There used to be this name in posix.nim a long time ago, not sure why! -{.deprecated: [cSIG_HOLD: SIG_HOLD].} when StatHasNanoseconds: proc st_atime*(s: Stat): Time {.inline.} = diff --git a/lib/posix/posix_linux_amd64.nim b/lib/posix/posix_linux_amd64.nim index 6e69409ea..f37ddfa37 100644 --- a/lib/posix/posix_linux_amd64.nim +++ b/lib/posix/posix_linux_amd64.nim @@ -27,13 +27,10 @@ type DIR* {.importc: "DIR", header: "<dirent.h>", incompleteStruct.} = object ## A type representing a directory stream. -{.deprecated: [TDIR: DIR].} type SocketHandle* = distinct cint # The type used to represent socket descriptors -{.deprecated: [TSocketHandle: SocketHandle].} - # not detected by detect.nim, guarded by #ifdef __USE_UNIX98 in glibc const SIG_HOLD* = cast[SigHandler](2) @@ -375,29 +372,6 @@ type ## context is active. # todo fpregds_mem -{.deprecated: [TOff: Off, TPid: Pid, TGid: Gid, TMode: Mode, TDev: Dev, - TNlink: Nlink, TStack: Stack, TGroup: Group, TMqd: Mqd, - TPasswd: Passwd, TClock: Clock, TClockId: ClockId, TKey: Key, - TSem: Sem, Tpthread_attr: PthreadAttr, Ttimespec: Timespec, - Tdirent: Dirent, TGlob: Glob, - # Tflock: Flock, # Naming conflict if we drop the `T` - Ticonv: Iconv, Tlconv: Lconv, TMqAttr: MqAttr, Tblkcnt: Blkcnt, - Tblksize: Blksize, Tfsblkcnt: Fsblkcnt, Tfsfilcnt: Fsfilcnt, - Tid: Id, Tino: Ino, Tpthread_barrier: Pthread_barrier, - Tpthread_barrierattr: Pthread_barrierattr, Tpthread_cond: Pthread_cond, - TPthread_condattr: Pthread_condattr, Tpthread_key: Pthread_key, - Tpthread_mutex: Pthread_mutex, Tpthread_mutexattr: Pthread_mutexattr, - Tpthread_once: Pthread_once, Tpthread_rwlock: Pthread_rwlock, - Tpthread_rwlockattr: Pthread_rwlockattr, Tpthread_spinlock: Pthread_spinlock, - Tpthread: Pthread, Tsuseconds: Suseconds, Ttimer: Timer, - Tuid: Uid, Tuseconds: Useconds, Tutsname: Utsname, Tipc_perm: Ipc_perm, - TStat: Stat, TStatvfs: Statvfs, - Ttm: Tm, titimerspec: Itimerspec, Tsig_atomic: Sig_atomic, Tsigset: Sigset, - TsigEvent: SigEvent, TsigVal: SigVal, TSigaction: Sigaction, - TSigStack: SigStack, TsigInfo: SigInfo, Tnl_item: Nl_item, - Tnl_catd: Nl_catd, Tsched_param: Sched_param, - # TFdSet: FdSet, # Naming conflict if we drop the `T` - Tmcontext: Mcontext, Tucontext: Ucontext].} type Taiocb* {.importc: "struct aiocb", header: "<aio.h>", final, pure.} = object ## struct aiocb @@ -587,13 +561,6 @@ type Tnfds* {.importc: "nfds_t", header: "<poll.h>".} = culong -{.deprecated: [TSockaddr_in: Sockaddr_in, TAddrinfo: AddrInfo, - TSockAddr: SockAddr, TSockLen: SockLen, TTimeval: Timeval, - Tsockaddr_storage: Sockaddr_storage, Tsockaddr_in6: Sockaddr_in6, - Thostent: Hostent, TServent: Servent, - TInAddr: InAddr, TIOVec: IOVec, TInPort: InPort, TInAddrT: InAddrT, - TIn6Addr: In6Addr, TInAddrScalar: InAddrScalar, TProtoent: Protoent].} - var errno* {.importc, header: "<errno.h>".}: cint ## error variable h_errno* {.importc, header: "<netdb.h>".}: cint diff --git a/lib/posix/posix_other.nim b/lib/posix/posix_other.nim index ba1dd89ed..261550d96 100644 --- a/lib/posix/posix_other.nim +++ b/lib/posix/posix_other.nim @@ -26,13 +26,10 @@ type DIR* {.importc: "DIR", header: "<dirent.h>", incompleteStruct.} = object ## A type representing a directory stream. -{.deprecated: [TDIR: DIR].} type SocketHandle* = distinct cint # The type used to represent socket descriptors -{.deprecated: [TSocketHandle: SocketHandle].} - type Time* {.importc: "time_t", header: "<time.h>".} = distinct clong @@ -356,31 +353,7 @@ type uc_stack*: Stack ## The stack used by this context. uc_mcontext*: Mcontext ## A machine-specific representation of the saved ## context. -{.deprecated: [TOff: Off, TPid: Pid, TGid: Gid, TMode: Mode, TDev: Dev, - TNlink: Nlink, TStack: Stack, TGroup: Group, TMqd: Mqd, - TPasswd: Passwd, TClock: Clock, TClockId: ClockId, TKey: Key, - TSem: Sem, Tpthread_attr: PthreadAttr, Ttimespec: Timespec, - Tdirent: Dirent, TFTW: FTW, TGlob: Glob, - # Tflock: Flock, # Naming conflict if we drop the `T` - Ticonv: Iconv, Tlconv: Lconv, TMqAttr: MqAttr, Tblkcnt: Blkcnt, - Tblksize: Blksize, Tfsblkcnt: Fsblkcnt, Tfsfilcnt: Fsfilcnt, - Tid: Id, Tino: Ino, Tpthread_barrier: Pthread_barrier, - Tpthread_barrierattr: Pthread_barrierattr, Tpthread_cond: Pthread_cond, - TPthread_condattr: Pthread_condattr, Tpthread_key: Pthread_key, - Tpthread_mutex: Pthread_mutex, Tpthread_mutexattr: Pthread_mutexattr, - Tpthread_once: Pthread_once, Tpthread_rwlock: Pthread_rwlock, - Tpthread_rwlockattr: Pthread_rwlockattr, Tpthread_spinlock: Pthread_spinlock, - Tpthread: Pthread, Tsuseconds: Suseconds, Ttimer: Timer, - Ttrace_attr: Trace_attr, Ttrace_event_id: Trace_event_id, - Ttrace_event_set: Trace_event_set, Ttrace_id: Trace_id, - Tuid: Uid, Tuseconds: Useconds, Tutsname: Utsname, Tipc_perm: Ipc_perm, - TStat: Stat, TStatvfs: Statvfs, Tposix_typed_mem_info: Posix_typed_mem_info, - Ttm: Tm, titimerspec: Itimerspec, Tsig_atomic: Sig_atomic, Tsigset: Sigset, - TsigEvent: SigEvent, TsigVal: SigVal, TSigaction: Sigaction, - TSigStack: SigStack, TsigInfo: SigInfo, Tnl_item: Nl_item, - Tnl_catd: Nl_catd, Tsched_param: Sched_param, - # TFdSet: FdSet, # Naming conflict if we drop the `T` - Tmcontext: Mcontext, Tucontext: Ucontext].} + when hasAioH: type Taiocb* {.importc: "struct aiocb", header: "<aio.h>", @@ -553,13 +526,6 @@ type Tnfds* {.importc: "nfds_t", header: "<poll.h>".} = cint -{.deprecated: [TSockaddr_in: Sockaddr_in, TAddrinfo: AddrInfo, - TSockAddr: SockAddr, TSockLen: SockLen, TTimeval: Timeval, - Tsockaddr_storage: Sockaddr_storage, Tsockaddr_in6: Sockaddr_in6, - Thostent: Hostent, TServent: Servent, - TInAddr: InAddr, TIOVec: IOVec, TInPort: InPort, TInAddrT: InAddrT, - TIn6Addr: In6Addr, TInAddrScalar: InAddrScalar, TProtoent: Protoent].} - var errno* {.importc, header: "<errno.h>".}: cint ## error variable h_errno* {.importc, header: "<netdb.h>".}: cint diff --git a/lib/posix/termios.nim b/lib/posix/termios.nim index c08de7342..34bb47f93 100644 --- a/lib/posix/termios.nim +++ b/lib/posix/termios.nim @@ -13,7 +13,6 @@ import posix type Speed* = cuint Cflag* = cuint -{.deprecated: [Tcflag: Cflag].} const NCCS* = when defined(macosx): 20 else: 32 diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim index 5ef791cfe..aef4f1ce6 100644 --- a/lib/pure/asyncdispatch.nim +++ b/lib/pure/asyncdispatch.nim @@ -16,7 +16,8 @@ import asyncfutures except callSoon import nativesockets, net, deques export Port, SocketFlag -export asyncfutures, asyncstreams +export asyncfutures except callSoon +export asyncstreams #{.injectStmt: newGcInvariant().} @@ -199,7 +200,7 @@ proc adjustTimeout(pollTimeout: int, nextTimer: Option[int]): int {.inline.} = if pollTimeout == -1: return result = min(pollTimeout, result) -proc callSoon(cbproc: proc ()) {.gcsafe.} +proc callSoon*(cbproc: proc ()) {.gcsafe.} proc initCallSoonProc = if asyncfutures.getCallSoonProc().isNil: @@ -243,8 +244,6 @@ when defined(windows) or defined(nimdoc): AsyncEvent* = ptr AsyncEventImpl Callback = proc (fd: AsyncFD): bool {.closure,gcsafe.} - {.deprecated: [TCompletionKey: CompletionKey, TAsyncFD: AsyncFD, - TCustomOverlapped: CustomOverlapped, TCompletionData: CompletionData].} proc hash(x: AsyncFD): Hash {.borrow.} proc `==`*(x: AsyncFD, y: AsyncFD): bool {.borrow.} @@ -1079,7 +1078,6 @@ else: PDispatcher* = ref object of PDispatcherBase selector: Selector[AsyncData] - {.deprecated: [TAsyncFD: AsyncFD, TCallback: Callback].} proc `==`*(x, y: AsyncFD): bool {.borrow.} proc `==`*(x, y: AsyncEvent): bool {.borrow.} @@ -1640,7 +1638,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/asyncfutures.nim b/lib/pure/asyncfutures.nim index 965e70055..5037c8a24 100644 --- a/lib/pure/asyncfutures.nim +++ b/lib/pure/asyncfutures.nim @@ -123,11 +123,17 @@ proc add(callbacks: var CallbackList, function: CallbackFunc) = 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 + let newCallback = new(ref CallbackList) + newCallback.function = function + newCallback.next = nil + + if callbacks.next == nil: + callbacks.next = newCallback + else: + var last = callbacks.next + while last.next != nil: + last = last.next + last.next = newCallback proc complete*[T](future: Future[T], val: T) = ## Completes ``future`` with value ``val``. @@ -369,8 +375,9 @@ proc `or`*[T, Y](fut1: Future[T], fut2: Future[Y]): Future[void] = ## complete. var retFuture = newFuture[void]("asyncdispatch.`or`") proc cb[X](fut: Future[X]) = - if fut.failed: retFuture.fail(fut.error) - if not retFuture.finished: retFuture.complete() + if not retFuture.finished: + if fut.failed: retFuture.fail(fut.error) + else: retFuture.complete() fut1.callback = cb[T] fut2.callback = cb[Y] return retFuture diff --git a/lib/pure/cgi.nim b/lib/pure/cgi.nim index 101146ace..869abc9cc 100644 --- a/lib/pure/cgi.nim +++ b/lib/pure/cgi.nim @@ -147,6 +147,12 @@ proc readData*(allowedMethods: set[RequestMethod] = for name, value in decodeData(allowedMethods): result[name.string] = value.string +proc readData*(data: string): StringTableRef = + ## Read CGI data from a string. + result = newStringTable() + for name, value in decodeData(data): + result[name.string] = value.string + proc validateData*(data: StringTableRef, validKeys: varargs[string]) = ## validates data; raises `ECgi` if this fails. This checks that each variable ## name of the CGI `data` occurs in the `validKeys` array. diff --git a/lib/pure/collections/LockFreeHash.nim b/lib/pure/collections/LockFreeHash.nim index 954d62491..28fa2a81b 100644 --- a/lib/pure/collections/LockFreeHash.nim +++ b/lib/pure/collections/LockFreeHash.nim @@ -49,13 +49,11 @@ when sizeof(int) == 4: # 32bit Raw = range[0..1073741823] ## The range of uint values that can be stored directly in a value slot ## when on a 32 bit platform - {.deprecated: [TRaw: Raw].} elif sizeof(int) == 8: # 64bit type Raw = range[0'i64..4611686018427387903'i64] ## The range of uint values that can be stored directly in a value slot ## when on a 64 bit platform - {.deprecated: [TRaw: Raw].} else: {.error: "unsupported platform".} @@ -74,7 +72,6 @@ type copyDone: int next: PConcTable[K,V] data: EntryArr -{.deprecated: [TEntry: Entry, TEntryArr: EntryArr].} proc setVal[K,V](table: var PConcTable[K,V], key: int, val: int, expVal: int, match: bool): int @@ -544,7 +541,6 @@ when not defined(testing) and isMainModule: Data = tuple[k: string,v: TestObj] PDataArr = array[0..numTests-1, Data] Dict = PConcTable[string,TestObj] - {.deprecated: [TTestObj: TestObj, TData: Data].} var thr: array[0..numThreads-1, Thread[Dict]] diff --git a/lib/pure/collections/critbits.nim b/lib/pure/collections/critbits.nim index c94e08098..32e0299ba 100644 --- a/lib/pure/collections/critbits.nim +++ b/lib/pure/collections/critbits.nim @@ -33,8 +33,6 @@ type root: Node[T] count: int -{.deprecated: [TCritBitTree: CritBitTree].} - proc len*[T](c: CritBitTree[T]): int = ## returns the number of elements in `c` in O(1). result = c.count diff --git a/lib/pure/collections/intsets.nim b/lib/pure/collections/intsets.nim index 545958977..def96b8f7 100644 --- a/lib/pure/collections/intsets.nim +++ b/lib/pure/collections/intsets.nim @@ -44,8 +44,6 @@ type data: TrunkSeq a: array[0..33, int] # profiling shows that 34 elements are enough -{.deprecated: [TIntSet: IntSet, TTrunk: Trunk, TTrunkSeq: TrunkSeq].} - proc mustRehash(length, counter: int): bool {.inline.} = assert(length > counter) result = (length * 2 < counter * 3) or (length - counter < 4) diff --git a/lib/pure/collections/lists.nim b/lib/pure/collections/lists.nim index e69acc8d9..0b3708a7c 100644 --- a/lib/pure/collections/lists.nim +++ b/lib/pure/collections/lists.nim @@ -45,15 +45,6 @@ type SomeLinkedNode*[T] = SinglyLinkedNode[T] | DoublyLinkedNode[T] -{.deprecated: [TDoublyLinkedNode: DoublyLinkedNodeObj, - PDoublyLinkedNode: DoublyLinkedNode, - TSinglyLinkedNode: SinglyLinkedNodeObj, - PSinglyLinkedNode: SinglyLinkedNode, - TDoublyLinkedList: DoublyLinkedList, - TSinglyLinkedRing: SinglyLinkedRing, - TDoublyLinkedRing: DoublyLinkedRing, - TSinglyLinkedList: SinglyLinkedList].} - proc initSinglyLinkedList*[T](): SinglyLinkedList[T] = ## creates a new singly linked list that is empty. discard diff --git a/lib/pure/collections/queues.nim b/lib/pure/collections/queues.nim index ce792d6da..9a1d169fb 100644 --- a/lib/pure/collections/queues.nim +++ b/lib/pure/collections/queues.nim @@ -46,8 +46,6 @@ type data: seq[T] rd, wr, count, mask: int -{.deprecated: [TQueue: Queue].} - proc initQueue*[T](initialSize: int = 4): Queue[T] = ## Create a new queue. ## Optionally, the initial capacity can be reserved via `initialSize` as a diff --git a/lib/pure/collections/rtarrays.nim b/lib/pure/collections/rtarrays.nim index 3849117a0..90dbf0049 100644 --- a/lib/pure/collections/rtarrays.nim +++ b/lib/pure/collections/rtarrays.nim @@ -19,7 +19,6 @@ type L: Natural spart: seq[T] apart: array[ArrayPartSize, 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 e8ea675f5..be10780ff 100644 --- a/lib/pure/collections/sequtils.nim +++ b/lib/pure/collections/sequtils.nim @@ -504,36 +504,76 @@ template anyIt*(s, pred: untyped): bool = break result -template toSeq*(iter: untyped): untyped = - ## Transforms any iterator into a sequence. - ## - ## Example: - ## - ## .. code-block:: - ## let - ## numeric = @[1, 2, 3, 4, 5, 6, 7, 8, 9] - ## odd_numbers = toSeq(filter(numeric) do (x: int) -> bool: - ## if x mod 2 == 1: - ## result = true) - ## assert odd_numbers == @[1, 3, 5, 7, 9] - - # Note: see also `mapIt` for explanation of some of the implementation - # subtleties. - when compiles(iter.len): +template toSeq1(s: not iterator): untyped = + # overload for typed but not iterator + type outType = type(items(s)) + when compiles(s.len): block: - evalOnceAs(iter2, iter, true) - var result = newSeq[type(iter)](iter2.len) + evalOnceAs(s2, s, compiles((let _ = s))) var i = 0 - for x in iter2: - result[i] = x - inc i + var result = newSeq[outType](s2.len) + for it in s2: + result[i] = it + i += 1 result else: - var result: seq[type(iter)] = @[] - for x in iter: - result.add(x) + var result: seq[outType] = @[] + for it in s: + result.add(it) + result + +template toSeq2(iter: iterator): untyped = + # overload for iterator + evalOnceAs(iter2, iter(), false) + when compiles(iter2.len): + var i = 0 + var result = newSeq[type(iter2)](iter2.len) + for x in iter2: + result[i] = x + inc i + result + else: + type outType = type(iter2()) + var result: seq[outType] = @[] + when compiles(iter2()): + evalOnceAs(iter4, iter, false) + let iter3=iter4() + for x in iter3(): + result.add(x) + else: + for x in iter2(): + result.add(x) result +template toSeq*(iter: untyped): untyped = + ## Transforms any iterable into a sequence. + runnableExamples: + let + numeric = @[1, 2, 3, 4, 5, 6, 7, 8, 9] + odd_numbers = toSeq(filter(numeric, proc(x: int): bool = x mod 2 == 1)) + doAssert odd_numbers == @[1, 3, 5, 7, 9] + + when compiles(toSeq1(iter)): + toSeq1(iter) + elif compiles(toSeq2(iter)): + toSeq2(iter) + else: + # overload for untyped, eg: `toSeq(myInlineIterator(3))` + when compiles(iter.len): + block: + evalOnceAs(iter2, iter, true) + var result = newSeq[type(iter)](iter2.len) + var i = 0 + for x in iter2: + result[i] = x + inc i + result + else: + var result: seq[type(iter)] = @[] + for x in iter: + result.add(x) + result + template foldl*(sequence, operation: untyped): untyped = ## Template to fold a sequence from left to right, returning the accumulation. ## @@ -1033,12 +1073,72 @@ when isMainModule: assert anyIt(anumbers, it > 9) == false block: # toSeq test - let - numeric = @[1, 2, 3, 4, 5, 6, 7, 8, 9] - odd_numbers = toSeq(filter(numeric) do (x: int) -> bool: - if x mod 2 == 1: - result = true) - assert odd_numbers == @[1, 3, 5, 7, 9] + block: + let + numeric = @[1, 2, 3, 4, 5, 6, 7, 8, 9] + odd_numbers = toSeq(filter(numeric) do (x: int) -> bool: + if x mod 2 == 1: + result = true) + assert odd_numbers == @[1, 3, 5, 7, 9] + + block: + doAssert [1,2].toSeq == @[1,2] + doAssert @[1,2].toSeq == @[1,2] + + doAssert @[1,2].toSeq == @[1,2] + doAssert toSeq(@[1,2]) == @[1,2] + + block: + iterator myIter(seed:int):auto= + for i in 0..<seed: + yield i + doAssert toSeq(myIter(2)) == @[0, 1] + + block: + iterator myIter():auto{.inline.}= + yield 1 + yield 2 + + doAssert myIter.toSeq == @[1,2] + doAssert toSeq(myIter) == @[1,2] + + block: + iterator myIter():int {.closure.} = + yield 1 + yield 2 + + doAssert myIter.toSeq == @[1,2] + doAssert toSeq(myIter) == @[1,2] + + block: + proc myIter():auto= + iterator ret():int{.closure.}= + yield 1 + yield 2 + result = ret + + doAssert myIter().toSeq == @[1,2] + doAssert toSeq(myIter()) == @[1,2] + + block: + proc myIter(n:int):auto= + var counter = 0 + iterator ret():int{.closure.}= + while counter<n: + yield counter + counter.inc + result = ret + + block: + let myIter3 = myIter(3) + doAssert myIter3.toSeq == @[0,1,2] + block: + let myIter3 = myIter(3) + doAssert toSeq(myIter3) == @[0,1,2] + block: + # makes sure this does not hang forever + doAssert myIter(3).toSeq == @[0,1,2] + doAssert toSeq(myIter(3)) == @[0,1,2] block: # tests https://github.com/nim-lang/Nim/issues/7187 diff --git a/lib/pure/collections/sets.nim b/lib/pure/collections/sets.nim index 7355aae02..1273cbc33 100644 --- a/lib/pure/collections/sets.nim +++ b/lib/pure/collections/sets.nim @@ -39,8 +39,6 @@ type data: KeyValuePairSeq[A] counter: int -{.deprecated: [TSet: HashSet].} - template default[T](t: typedesc[T]): T = ## Used by clear methods to get a default value. var v: T @@ -631,8 +629,6 @@ type data: OrderedKeyValuePairSeq[A] counter, first, last: int -{.deprecated: [TOrderedSet: OrderedSet].} - proc clear*[A](s: var OrderedSet[A]) = ## Clears the OrderedSet back to an empty state, without shrinking ## any of the existing storage. O(n) where n is the size of the hash bucket. diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim index 9fdae33ed..f46a368b1 100644 --- a/lib/pure/collections/tables.nim +++ b/lib/pure/collections/tables.nim @@ -125,8 +125,6 @@ type counter: int TableRef*[A,B] = ref Table[A, B] -{.deprecated: [TTable: Table, PTable: TableRef].} - template maxHash(t): untyped = high(t.data) template dataLen(t): untyped = len(t.data) @@ -520,8 +518,6 @@ type counter, first, last: int OrderedTableRef*[A, B] = ref OrderedTable[A, B] -{.deprecated: [TOrderedTable: OrderedTable, POrderedTable: OrderedTableRef].} - proc len*[A, B](t: OrderedTable[A, B]): int {.inline.} = ## returns the number of keys in ``t``. result = t.counter @@ -795,7 +791,7 @@ proc getOrDefault*[A, B](t: OrderedTableRef[A, B], key: A): B = getOrDefault(t[], key) proc getOrDefault*[A, B](t: OrderedTableRef[A, B], key: A, default: B): B = - ## retrieves the value at ``t[key]`` iff ``key`` is in ``t``. Otherwise, + ## retrieves the value at ``t[key]`` iff ``key`` is in ``t``. Otherwise, ## ``default`` is returned. getOrDefault(t[], key, default) @@ -892,8 +888,6 @@ type counter: int CountTableRef*[A] = ref CountTable[A] -{.deprecated: [TCountTable: CountTable, PCountTable: CountTableRef].} - proc len*[A](t: CountTable[A]): int = ## returns the number of keys in ``t``. result = t.counter diff --git a/lib/pure/complex.nim b/lib/pure/complex.nim index ba5c571ce..69d9c0f7f 100644 --- a/lib/pure/complex.nim +++ b/lib/pure/complex.nim @@ -9,78 +9,109 @@ -## This module implements complex numbers. -{.push checks:off, line_dir:off, stack_trace:off, debugger:off.} -# the user does not want to trace a part -# of the standard library! -import - math +## This module implements complex numbers. +## Complex numbers are currently implemented as generic on a 64-bit or 32-bit float. + +{.push checks: off, line_dir: off, stack_trace: off, debugger: off.} +# the user does not want to trace a part of the standard library! -const - EPS = 1.0e-7 ## Epsilon used for float comparisons. +import math type - Complex* = tuple[re, im: float] - ## a complex number, consisting of a real and an imaginary part - -const - im*: Complex = (re: 0.0, im: 1.0) - ## The imaginary unit. √-1. + Complex*[T: SomeFloat] = object + re*, im*: T + ## A complex number, consisting of a real and an imaginary part. + Complex64* = Complex[float64] + ## Alias for a pair of 64-bit floats. + Complex32* = Complex[float32] + ## Alias for a pair of 32-bit floats. + +proc complex*[T: SomeFloat](re: T; im: T = 0.0): Complex[T] = + result.re = re + result.im = im + +proc complex32*(re: float32; im: float32 = 0.0): Complex[float32] = + result.re = re + result.im = im + +proc complex64*(re: float64; im: float64 = 0.0): Complex[float64] = + result.re = re + result.im = im + +template im*(arg: typedesc[float32]): Complex32 = complex[float32](0, 1) +template im*(arg: typedesc[float64]): Complex64 = complex[float64](0, 1) +template im*(arg : float32): Complex32 = complex[float32](0, arg) +template im*(arg : float64): Complex64 = complex[float64](0, arg) + +proc abs*[T](z: Complex[T]): T = + ## Return the distance from (0,0) to ``z``. + result = hypot(z.re, z.im) + +proc abs2*[T](z: Complex[T]): T = + ## Return the squared distance from (0,0) to ``z``. + result = z.re*z.re + z.im*z.im + +proc conjugate*[T](z: Complex[T]): Complex[T] = + ## Conjugate of complex number ``z``. + result.re = z.re + result.im = -z.im -proc toComplex*(x: SomeInteger): Complex = - ## Convert some integer ``x`` to a complex number. - result.re = x - result.im = 0 +proc inv*[T](z: Complex[T]): Complex[T] = + ## Multiplicative inverse of complex number ``z``. + conjugate(z) / abs2(z) -proc `==` *(x, y: Complex): bool = - ## Compare two complex numbers `x` and `y` for equality. +proc `==` *[T](x, y: Complex[T]): bool = + ## Compare two complex numbers ``x`` and ``y`` for equality. result = x.re == y.re and x.im == y.im -proc `=~` *(x, y: Complex): bool = - ## Compare two complex numbers `x` and `y` approximately. - result = abs(x.re-y.re)<EPS and abs(x.im-y.im)<EPS - -proc `+` *(x, y: Complex): Complex = - ## Add two complex numbers. - result.re = x.re + y.re - result.im = x.im + y.im +proc `+` *[T](x: T, y: Complex[T]): Complex[T] = + ## Add a real number to a complex number. + result.re = x + y.re + result.im = y.im -proc `+` *(x: Complex, y: float): Complex = - ## Add complex `x` to float `y`. +proc `+` *[T](x: Complex[T], y: T): Complex[T] = + ## Add a complex number to a real number. result.re = x.re + y result.im = x.im -proc `+` *(x: float, y: Complex): Complex = - ## Add float `x` to complex `y`. - result.re = x + y.re - result.im = y.im - +proc `+` *[T](x, y: Complex[T]): Complex[T] = + ## Add two complex numbers. + result.re = x.re + y.re + result.im = x.im + y.im -proc `-` *(z: Complex): Complex = +proc `-` *[T](z: Complex[T]): Complex[T] = ## Unary minus for complex numbers. result.re = -z.re result.im = -z.im -proc `-` *(x, y: Complex): Complex = +proc `-` *[T](x: T, y: Complex[T]): Complex[T] = + ## Subtract a complex number from a real number. + x + (-y) + +proc `-` *[T](x: Complex[T], y: T): Complex[T] = + ## Subtract a real number from a complex number. + result.re = x.re - y + result.im = x.im + +proc `-` *[T](x, y: Complex[T]): Complex[T] = ## Subtract two complex numbers. result.re = x.re - y.re result.im = x.im - y.im -proc `-` *(x: Complex, y: float): Complex = - ## Subtracts float `y` from complex `x`. - result = x + (-y) - -proc `-` *(x: float, y: Complex): Complex = - ## Subtracts complex `y` from float `x`. - result = x + (-y) +proc `/` *[T](x: Complex[T], y: T): Complex[T] = + ## Divide complex number ``x`` by real number ``y``. + result.re = x.re / y + result.im = x.im / y +proc `/` *[T](x: T, y: Complex[T]): Complex[T] = + ## Divide real number ``x`` by complex number ``y``. + result = x * inv(y) -proc `/` *(x, y: Complex): Complex = - ## Divide `x` by `y`. - var - r, den: float +proc `/` *[T](x, y: Complex[T]): Complex[T] = + ## Divide ``x`` by ``y``. + var r, den: T if abs(y.re) < abs(y.im): r = y.re / y.im den = y.im + r * y.re @@ -92,101 +123,46 @@ proc `/` *(x, y: Complex): Complex = result.re = (x.re + r * x.im) / den result.im = (x.im - r * x.re) / den -proc `/` *(x : Complex, y: float ): Complex = - ## Divide complex `x` by float `y`. - result.re = x.re/y - result.im = x.im/y - -proc `/` *(x : float, y: Complex ): Complex = - ## Divide float `x` by complex `y`. - var num : Complex = (x, 0.0) - result = num/y - - -proc `*` *(x, y: Complex): Complex = - ## Multiply `x` with `y`. - result.re = x.re * y.re - x.im * y.im - result.im = x.im * y.re + x.re * y.im - -proc `*` *(x: float, y: Complex): Complex = - ## Multiply float `x` with complex `y`. +proc `*` *[T](x: T, y: Complex[T]): Complex[T] = + ## Multiply a real number and a complex number. result.re = x * y.re result.im = x * y.im -proc `*` *(x: Complex, y: float): Complex = - ## Multiply complex `x` with float `y`. +proc `*` *[T](x: Complex[T], y: T): Complex[T] = + ## Multiply a complex number with a real number. result.re = x.re * y result.im = x.im * y +proc `*` *[T](x, y: Complex[T]): Complex[T] = + ## Multiply ``x`` with ``y``. + result.re = x.re * y.re - x.im * y.im + result.im = x.im * y.re + x.re * y.im -proc `+=` *(x: var Complex, y: Complex) = - ## Add `y` to `x`. + +proc `+=` *[T](x: var Complex[T], y: Complex[T]) = + ## Add ``y`` to ``x``. x.re += y.re x.im += y.im -proc `+=` *(x: var Complex, y: float) = - ## Add `y` to the complex number `x`. - x.re += y - -proc `-=` *(x: var Complex, y: Complex) = - ## Subtract `y` from `x`. +proc `-=` *[T](x: var Complex[T], y: Complex[T]) = + ## Subtract ``y`` from ``x``. x.re -= y.re x.im -= y.im -proc `-=` *(x: var Complex, y: float) = - ## Subtract `y` from the complex number `x`. - x.re -= y - -proc `*=` *(x: var Complex, y: Complex) = - ## Multiply `y` to `x`. +proc `*=` *[T](x: var Complex[T], y: Complex[T]) = + ## Multiply ``y`` to ``x``. let im = x.im * y.re + x.re * y.im x.re = x.re * y.re - x.im * y.im x.im = im -proc `*=` *(x: var Complex, y: float) = - ## Multiply `y` to the complex number `x`. - x.re *= y - x.im *= y - -proc `/=` *(x: var Complex, y: Complex) = - ## Divide `x` by `y` in place. +proc `/=` *[T](x: var Complex[T], y: Complex[T]) = + ## Divide ``x`` by ``y`` in place. x = x / y -proc `/=` *(x : var Complex, y: float) = - ## Divide complex `x` by float `y` in place. - x.re /= y - x.im /= y - - -proc abs*(z: Complex): float = - ## Return the distance from (0,0) to `z`. - # optimized by checking special cases (sqrt is expensive) - var x, y, temp: float - - x = abs(z.re) - y = abs(z.im) - if x == 0.0: - result = y - elif y == 0.0: - result = x - elif x > y: - temp = y / x - result = x * sqrt(1.0 + temp * temp) - else: - temp = x / y - result = y * sqrt(1.0 + temp * temp) - - -proc conjugate*(z: Complex): Complex = - ## Conjugate of complex number `z`. - result.re = z.re - result.im = -z.im - - -proc sqrt*(z: Complex): Complex = - ## Square root for a complex number `z`. - var x, y, w, r: float +proc sqrt*[T](z: Complex[T]): Complex[T] = + ## Square root for a complex number ``z``. + var x, y, w, r: T if z.re == 0.0 and z.im == 0.0: result = z @@ -199,247 +175,283 @@ proc sqrt*(z: Complex): Complex = else: r = x / y w = sqrt(y) * sqrt(0.5 * (r + sqrt(1.0 + r * r))) + if z.re >= 0.0: result.re = w result.im = z.im / (w * 2.0) else: - if z.im >= 0.0: result.im = w - else: result.im = -w + result.im = if z.im >= 0.0: w else: -w result.re = z.im / (result.im + result.im) +proc exp*[T](z: Complex[T]): Complex[T] = + ## ``e`` raised to the power ``z``. + var + rho = exp(z.re) + theta = z.im + result.re = rho * cos(theta) + result.im = rho * sin(theta) -proc exp*(z: Complex): Complex = - ## e raised to the power `z`. - var rho = exp(z.re) - var theta = z.im - result.re = rho*cos(theta) - result.im = rho*sin(theta) - - -proc ln*(z: Complex): Complex = - ## Returns the natural log of `z`. +proc ln*[T](z: Complex[T]): Complex[T] = + ## Returns the natural log of ``z``. result.re = ln(abs(z)) - result.im = arctan2(z.im,z.re) - -proc log10*(z: Complex): Complex = - ## Returns the log base 10 of `z`. - result = ln(z)/ln(10.0) + result.im = arctan2(z.im, z.re) -proc log2*(z: Complex): Complex = - ## Returns the log base 2 of `z`. - result = ln(z)/ln(2.0) +proc log10*[T](z: Complex[T]): Complex[T] = + ## Returns the log base 10 of ``z``. + result = ln(z) / ln(10.0) +proc log2*[T](z: Complex[T]): Complex[T] = + ## Returns the log base 2 of ``z``. + result = ln(z) / ln(2.0) -proc pow*(x, y: Complex): Complex = - ## `x` raised to the power `y`. - if x.re == 0.0 and x.im == 0.0: - if y.re == 0.0 and y.im == 0.0: +proc pow*[T](x, y: Complex[T]): Complex[T] = + ## ``x`` raised to the power ``y``. + if x.re == 0.0 and x.im == 0.0: + if y.re == 0.0 and y.im == 0.0: result.re = 1.0 result.im = 0.0 else: result.re = 0.0 result.im = 0.0 - elif y.re == 1.0 and y.im == 0.0: + elif y.re == 1.0 and y.im == 0.0: result = x - elif y.re == -1.0 and y.im == 0.0: - result = 1.0/x + elif y.re == -1.0 and y.im == 0.0: + result = T(1.0) / x else: - var rho = sqrt(x.re*x.re + x.im*x.im) - var theta = arctan2(x.im,x.re) - var s = pow(rho,y.re) * exp(-y.im*theta) - var r = y.re*theta + y.im*ln(rho) - result.re = s*cos(r) - result.im = s*sin(r) - - -proc sin*(z: Complex): Complex = - ## Returns the sine of `z`. - result.re = sin(z.re)*cosh(z.im) - result.im = cos(z.re)*sinh(z.im) - -proc arcsin*(z: Complex): Complex = - ## Returns the inverse sine of `z`. - var i: Complex = (0.0,1.0) - result = -i*ln(i*z + sqrt(1.0-z*z)) - -proc cos*(z: Complex): Complex = - ## Returns the cosine of `z`. - result.re = cos(z.re)*cosh(z.im) - result.im = -sin(z.re)*sinh(z.im) - -proc arccos*(z: Complex): Complex = - ## Returns the inverse cosine of `z`. - var i: Complex = (0.0,1.0) - result = -i*ln(z + sqrt(z*z-1.0)) - -proc tan*(z: Complex): Complex = - ## Returns the tangent of `z`. - result = sin(z)/cos(z) - -proc arctan*(z: Complex): Complex = - ## Returns the inverse tangent of `z`. - var i: Complex = (0.0,1.0) - result = 0.5*i*(ln(1-i*z)-ln(1+i*z)) - -proc cot*(z: Complex): Complex = - ## Returns the cotangent of `z`. + var + rho = abs(x) + theta = arctan2(x.im, x.re) + s = pow(rho, y.re) * exp(-y.im * theta) + r = y.re * theta + y.im * ln(rho) + result.re = s * cos(r) + result.im = s * sin(r) + +proc pow*[T](x: Complex[T], y: T): Complex[T] = + ## Complex number ``x`` raised to the power ``y``. + pow(x, complex[T](y)) + + +proc sin*[T](z: Complex[T]): Complex[T] = + ## Returns the sine of ``z``. + result.re = sin(z.re) * cosh(z.im) + result.im = cos(z.re) * sinh(z.im) + +proc arcsin*[T](z: Complex[T]): Complex[T] = + ## Returns the inverse sine of ``z``. + result = -im(T) * ln(im(T) * z + sqrt(T(1.0) - z*z)) + +proc cos*[T](z: Complex[T]): Complex[T] = + ## Returns the cosine of ``z``. + result.re = cos(z.re) * cosh(z.im) + result.im = -sin(z.re) * sinh(z.im) + +proc arccos*[T](z: Complex[T]): Complex[T] = + ## Returns the inverse cosine of ``z``. + result = -im(T) * ln(z + sqrt(z*z - T(1.0))) + +proc tan*[T](z: Complex[T]): Complex[T] = + ## Returns the tangent of ``z``. + result = sin(z) / cos(z) + +proc arctan*[T](z: Complex[T]): Complex[T] = + ## Returns the inverse tangent of ``z``. + result = T(0.5)*im(T) * (ln(T(1.0) - im(T)*z) - ln(T(1.0) + im(T)*z)) + +proc cot*[T](z: Complex[T]): Complex[T] = + ## Returns the cotangent of ``z``. result = cos(z)/sin(z) -proc arccot*(z: Complex): Complex = - ## Returns the inverse cotangent of `z`. - var i: Complex = (0.0,1.0) - result = 0.5*i*(ln(1-i/z)-ln(1+i/z)) +proc arccot*[T](z: Complex[T]): Complex[T] = + ## Returns the inverse cotangent of ``z``. + result = T(0.5)*im(T) * (ln(T(1.0) - im(T)/z) - ln(T(1.0) + im(T)/z)) -proc sec*(z: Complex): Complex = - ## Returns the secant of `z`. - result = 1.0/cos(z) +proc sec*[T](z: Complex[T]): Complex[T] = + ## Returns the secant of ``z``. + result = T(1.0) / cos(z) -proc arcsec*(z: Complex): Complex = - ## Returns the inverse secant of `z`. - var i: Complex = (0.0,1.0) - result = -i*ln(i*sqrt(1-1/(z*z))+1/z) +proc arcsec*[T](z: Complex[T]): Complex[T] = + ## Returns the inverse secant of ``z``. + result = -im(T) * ln(im(T) * sqrt(1.0 - 1.0/(z*z)) + T(1.0)/z) -proc csc*(z: Complex): Complex = - ## Returns the cosecant of `z`. - result = 1.0/sin(z) +proc csc*[T](z: Complex[T]): Complex[T] = + ## Returns the cosecant of ``z``. + result = T(1.0) / sin(z) -proc arccsc*(z: Complex): Complex = - ## Returns the inverse cosecant of `z`. - var i: Complex = (0.0,1.0) - result = -i*ln(sqrt(1-1/(z*z))+i/z) +proc arccsc*[T](z: Complex[T]): Complex[T] = + ## Returns the inverse cosecant of ``z``. + result = -im(T) * ln(sqrt(T(1.0) - T(1.0)/(z*z)) + im(T)/z) +proc sinh*[T](z: Complex[T]): Complex[T] = + ## Returns the hyperbolic sine of ``z``. + result = T(0.5) * (exp(z) - exp(-z)) -proc sinh*(z: Complex): Complex = - ## Returns the hyperbolic sine of `z`. - result = 0.5*(exp(z)-exp(-z)) +proc arcsinh*[T](z: Complex[T]): Complex[T] = + ## Returns the inverse hyperbolic sine of ``z``. + result = ln(z + sqrt(z*z + 1.0)) -proc arcsinh*(z: Complex): Complex = - ## Returns the inverse hyperbolic sine of `z`. - result = ln(z+sqrt(z*z+1)) +proc cosh*[T](z: Complex[T]): Complex[T] = + ## Returns the hyperbolic cosine of ``z``. + result = T(0.5) * (exp(z) + exp(-z)) -proc cosh*(z: Complex): Complex = - ## Returns the hyperbolic cosine of `z`. - result = 0.5*(exp(z)+exp(-z)) +proc arccosh*[T](z: Complex[T]): Complex[T] = + ## Returns the inverse hyperbolic cosine of ``z``. + result = ln(z + sqrt(z*z - T(1.0))) -proc arccosh*(z: Complex): Complex = - ## Returns the inverse hyperbolic cosine of `z`. - result = ln(z+sqrt(z*z-1)) +proc tanh*[T](z: Complex[T]): Complex[T] = + ## Returns the hyperbolic tangent of ``z``. + result = sinh(z) / cosh(z) -proc tanh*(z: Complex): Complex = - ## Returns the hyperbolic tangent of `z`. - result = sinh(z)/cosh(z) +proc arctanh*[T](z: Complex[T]): Complex[T] = + ## Returns the inverse hyperbolic tangent of ``z``. + result = T(0.5) * (ln((T(1.0)+z) / (T(1.0)-z))) -proc arctanh*(z: Complex): Complex = - ## Returns the inverse hyperbolic tangent of `z`. - result = 0.5*(ln((1+z)/(1-z))) +proc sech*[T](z: Complex[T]): Complex[T] = + ## Returns the hyperbolic secant of ``z``. + result = T(2.0) / (exp(z) + exp(-z)) -proc sech*(z: Complex): Complex = - ## Returns the hyperbolic secant of `z`. - result = 2/(exp(z)+exp(-z)) +proc arcsech*[T](z: Complex[T]): Complex[T] = + ## Returns the inverse hyperbolic secant of ``z``. + result = ln(1.0/z + sqrt(T(1.0)/z+T(1.0)) * sqrt(T(1.0)/z-T(1.0))) -proc arcsech*(z: Complex): Complex = - ## Returns the inverse hyperbolic secant of `z`. - result = ln(1/z+sqrt(1/z+1)*sqrt(1/z-1)) +proc csch*[T](z: Complex[T]): Complex[T] = + ## Returns the hyperbolic cosecant of ``z``. + result = T(2.0) / (exp(z) - exp(-z)) -proc csch*(z: Complex): Complex = - ## Returns the hyperbolic cosecant of `z`. - result = 2/(exp(z)-exp(-z)) +proc arccsch*[T](z: Complex[T]): Complex[T] = + ## Returns the inverse hyperbolic cosecant of ``z``. + result = ln(T(1.0)/z + sqrt(T(1.0)/(z*z) + T(1.0))) -proc arccsch*(z: Complex): Complex = - ## Returns the inverse hyperbolic cosecant of `z`. - result = ln(1/z+sqrt(1/(z*z)+1)) +proc coth*[T](z: Complex[T]): Complex[T] = + ## Returns the hyperbolic cotangent of ``z``. + result = cosh(z) / sinh(z) -proc coth*(z: Complex): Complex = - ## Returns the hyperbolic cotangent of `z`. - result = cosh(z)/sinh(z) +proc arccoth*[T](z: Complex[T]): Complex[T] = + ## Returns the inverse hyperbolic cotangent of ``z``. + result = T(0.5) * (ln(T(1.0) + T(1.0)/z) - ln(T(1.0) - T(1.0)/z)) -proc arccoth*(z: Complex): Complex = - ## Returns the inverse hyperbolic cotangent of `z`. - result = 0.5*(ln(1+1/z)-ln(1-1/z)) - -proc phase*(z: Complex): float = - ## Returns the phase of `z`. +proc phase*[T](z: Complex[T]): T = + ## Returns the phase of ``z``. arctan2(z.im, z.re) -proc polar*(z: Complex): tuple[r, phi: float] = - ## Returns `z` in polar coordinates. - result.r = abs(z) - result.phi = phase(z) +proc polar*[T](z: Complex[T]): tuple[r, phi: T] = + ## Returns ``z`` in polar coordinates. + (r: abs(z), phi: phase(z)) -proc rect*(r: float, phi: float): Complex = - ## Returns the complex number with polar coordinates `r` and `phi`. - result.re = r * cos(phi) - result.im = r * sin(phi) +proc rect*[T](r, phi: T): Complex[T] = + ## Returns the complex number with polar coordinates ``r`` and ``phi``. + ## + ## | ``result.re = r * cos(phi)`` + ## | ``result.im = r * sin(phi)`` + complex(r * cos(phi), r * sin(phi)) proc `$`*(z: Complex): string = - ## Returns `z`'s string representation as ``"(re, im)"``. + ## Returns ``z``'s string representation as ``"(re, im)"``. result = "(" & $z.re & ", " & $z.im & ")" {.pop.} when isMainModule: - var z = (0.0, 0.0) - var oo = (1.0,1.0) - var a = (1.0, 2.0) - var b = (-1.0, -2.0) - var m1 = (-1.0, 0.0) - var i = (0.0,1.0) - var one = (1.0,0.0) - var tt = (10.0, 20.0) - var ipi = (0.0, -PI) - - assert( a == a ) - assert( (a-a) == z ) - assert( (a+b) == z ) - assert( (a/b) == m1 ) - assert( (1.0/a) == (0.2, -0.4) ) - assert( (a*b) == (3.0, -4.0) ) - assert( 10.0*a == tt ) - assert( a*10.0 == tt ) - assert( tt/10.0 == a ) - assert( oo+(-1.0) == i ) - assert( (-1.0)+oo == i ) - assert( abs(oo) == sqrt(2.0) ) - assert( conjugate(a) == (1.0, -2.0) ) - assert( sqrt(m1) == i ) - assert( exp(ipi) =~ m1 ) - - assert( pow(a,b) =~ (-3.72999124927876, -1.68815826725068) ) - assert( pow(z,a) =~ (0.0, 0.0) ) - assert( pow(z,z) =~ (1.0, 0.0) ) - assert( pow(a,one) =~ a ) - assert( pow(a,m1) =~ (0.2, -0.4) ) - - assert( ln(a) =~ (0.804718956217050, 1.107148717794090) ) - assert( log10(a) =~ (0.349485002168009, 0.480828578784234) ) - assert( log2(a) =~ (1.16096404744368, 1.59727796468811) ) - - assert( sin(a) =~ (3.16577851321617, 1.95960104142161) ) - assert( cos(a) =~ (2.03272300701967, -3.05189779915180) ) - assert( tan(a) =~ (0.0338128260798967, 1.0147936161466335) ) - assert( cot(a) =~ 1.0/tan(a) ) - assert( sec(a) =~ 1.0/cos(a) ) - assert( csc(a) =~ 1.0/sin(a) ) - assert( arcsin(a) =~ (0.427078586392476, 1.528570919480998) ) - assert( arccos(a) =~ (1.14371774040242, -1.52857091948100) ) - assert( arctan(a) =~ (1.338972522294494, 0.402359478108525) ) - - assert( cosh(a) =~ (-0.642148124715520, 1.068607421382778) ) - assert( sinh(a) =~ (-0.489056259041294, 1.403119250622040) ) - assert( tanh(a) =~ (1.1667362572409199,-0.243458201185725) ) - assert( sech(a) =~ 1/cosh(a) ) - assert( csch(a) =~ 1/sinh(a) ) - assert( coth(a) =~ 1/tanh(a) ) - assert( arccosh(a) =~ (1.528570919480998, 1.14371774040242) ) - assert( arcsinh(a) =~ (1.469351744368185, 1.06344002357775) ) - assert( arctanh(a) =~ (0.173286795139986, 1.17809724509617) ) - assert( arcsech(a) =~ arccosh(1/a) ) - assert( arccsch(a) =~ arcsinh(1/a) ) - assert( arccoth(a) =~ arctanh(1/a) ) - - assert( phase(a) == 1.1071487177940904 ) + proc `=~`[T](x, y: Complex[T]): bool = + result = abs(x.re-y.re) < 1e-6 and abs(x.im-y.im) < 1e-6 + + proc `=~`[T](x: Complex[T], y: T): bool = + result = abs(x.re-y) < 1e-6 and abs(x.im) < 1e-6 + + var + z: Complex64 = complex(0.0, 0.0) + oo: Complex64 = complex(1.0, 1.0) + a: Complex64 = complex(1.0, 2.0) + b: Complex64 = complex(-1.0, -2.0) + m1: Complex64 = complex(-1.0, 0.0) + i: Complex64 = complex(0.0, 1.0) + one: Complex64 = complex(1.0, 0.0) + tt: Complex64 = complex(10.0, 20.0) + ipi: Complex64 = complex(0.0, -PI) + + doAssert(a/2.0 =~ complex(0.5, 1.0)) + doAssert(a == a) + doAssert((a-a) == z) + doAssert((a+b) == z) + doAssert((a+b) =~ 0.0) + doAssert((a/b) == m1) + doAssert((1.0/a) == complex(0.2, -0.4)) + doAssert((a*b) == complex(3.0, -4.0)) + doAssert(10.0*a == tt) + doAssert(a*10.0 == tt) + doAssert(tt/10.0 == a) + doAssert(oo+(-1.0) == i) + doAssert( (-1.0)+oo == i) + doAssert(abs(oo) == sqrt(2.0)) + doAssert(conjugate(a) == complex(1.0, -2.0)) + doAssert(sqrt(m1) == i) + doAssert(exp(ipi) =~ m1) + + doAssert(pow(a, b) =~ complex(-3.72999124927876, -1.68815826725068)) + doAssert(pow(z, a) =~ complex(0.0, 0.0)) + doAssert(pow(z, z) =~ complex(1.0, 0.0)) + doAssert(pow(a, one) =~ a) + doAssert(pow(a, m1) =~ complex(0.2, -0.4)) + doAssert(pow(a, 2.0) =~ complex(-3.0, 4.0)) + doAssert(pow(a, 2) =~ complex(-3.0, 4.0)) + doAssert(not(pow(a, 2.0) =~ a)) + + doAssert(ln(a) =~ complex(0.804718956217050, 1.107148717794090)) + doAssert(log10(a) =~ complex(0.349485002168009, 0.480828578784234)) + doAssert(log2(a) =~ complex(1.16096404744368, 1.59727796468811)) + + doAssert(sin(a) =~ complex(3.16577851321617, 1.95960104142161)) + doAssert(cos(a) =~ complex(2.03272300701967, -3.05189779915180)) + doAssert(tan(a) =~ complex(0.0338128260798967, 1.0147936161466335)) + doAssert(cot(a) =~ 1.0 / tan(a)) + doAssert(sec(a) =~ 1.0 / cos(a)) + doAssert(csc(a) =~ 1.0 / sin(a)) + doAssert(arcsin(a) =~ complex(0.427078586392476, 1.528570919480998)) + doAssert(arccos(a) =~ complex(1.14371774040242, -1.52857091948100)) + doAssert(arctan(a) =~ complex(1.338972522294494, 0.402359478108525)) + doAssert(arccot(a) =~ complex(0.2318238045004031, -0.402359478108525)) + doAssert(arcsec(a) =~ complex(1.384478272687081, 0.3965682301123288)) + doAssert(arccsc(a) =~ complex(0.1863180541078155, -0.3965682301123291)) + + doAssert(cosh(a) =~ complex(-0.642148124715520, 1.068607421382778)) + doAssert(sinh(a) =~ complex(-0.489056259041294, 1.403119250622040)) + doAssert(tanh(a) =~ complex(1.1667362572409199, -0.243458201185725)) + doAssert(sech(a) =~ 1.0 / cosh(a)) + doAssert(csch(a) =~ 1.0 / sinh(a)) + doAssert(coth(a) =~ 1.0 / tanh(a)) + doAssert(arccosh(a) =~ complex(1.528570919480998, 1.14371774040242)) + doAssert(arcsinh(a) =~ complex(1.469351744368185, 1.06344002357775)) + doAssert(arctanh(a) =~ complex(0.173286795139986, 1.17809724509617)) + doAssert(arcsech(a) =~ arccosh(1.0/a)) + doAssert(arccsch(a) =~ arcsinh(1.0/a)) + doAssert(arccoth(a) =~ arctanh(1.0/a)) + + doAssert(phase(a) == 1.1071487177940904) var t = polar(a) - assert( rect(t.r, t.phi) =~ a ) - assert( rect(1.0, 2.0) =~ (-0.4161468365471424, 0.9092974268256817) ) + doAssert(rect(t.r, t.phi) =~ a) + doAssert(rect(1.0, 2.0) =~ complex(-0.4161468365471424, 0.9092974268256817)) + + + var + i64: Complex32 = complex(0.0f, 1.0f) + a64: Complex32 = 2.0f*i64 + 1.0.float32 + b64: Complex32 = complex(-1.0'f32, -2.0'f32) + + doAssert(a64 == a64) + doAssert(a64 == -b64) + doAssert(a64 + b64 =~ 0.0'f32) + doAssert(not(pow(a64, b64) =~ a64)) + doAssert(pow(a64, 0.5f) =~ sqrt(a64)) + doAssert(pow(a64, 2) =~ complex(-3.0'f32, 4.0'f32)) + doAssert(sin(arcsin(b64)) =~ b64) + doAssert(cosh(arccosh(a64)) =~ a64) + + doAssert(phase(a64) - 1.107149f < 1e-6) + var t64 = polar(a64) + doAssert(rect(t64.r, t64.phi) =~ a64) + doAssert(rect(1.0f, 2.0f) =~ complex(-0.4161468f, 0.90929742f)) + doAssert(sizeof(a64) == 8) + doAssert(sizeof(a) == 16) + + doAssert 123.0.im + 456.0 == complex64(456, 123) diff --git a/lib/pure/coro.nim b/lib/pure/coro.nim index 2fe34ed40..d6a7ceec8 100644 --- a/lib/pure/coro.nim +++ b/lib/pure/coro.nim @@ -6,15 +6,17 @@ # See the file "copying.txt", included in this # distribution, for details about the copyright. # -## Nim coroutines implementation supports several context switching methods: -## ucontext: available on unix and alike (default) -## setjmp: available on unix and alike (x86/64 only) -## Fibers: available and required on windows. +## Nim coroutines implementation, supports several context switching methods: +## -------- ------------ +## ucontext available on unix and alike (default) +## setjmp available on unix and alike (x86/64 only) +## fibers available and required on windows. +## -------- ------------ ## -## -d:nimCoroutines Required to build this module. -## -d:nimCoroutinesUcontext Use ucontext backend. -## -d:nimCoroutinesSetjmp Use setjmp backend. -## -d:nimCoroutinesSetjmpBundled Use bundled setjmp implementation. +## -d:nimCoroutines Required to build this module. +## -d:nimCoroutinesUcontext Use ucontext backend. +## -d:nimCoroutinesSetjmp Use setjmp backend. +## -d:nimCoroutinesSetjmpBundled Use bundled setjmp implementation. when not nimCoroutines and not defined(nimdoc): when defined(noNimCoroutines): @@ -105,7 +107,7 @@ elif coroBackend == CORO_BACKEND_SETJMP: # Use setjmp/longjmp implementation provided by the system. type JmpBuf {.importc: "jmp_buf", header: "<setjmp.h>".} = object - + proc setjmp(ctx: var JmpBuf): int {.importc, header: "<setjmp.h>".} proc longjmp(ctx: JmpBuf, ret=1) {.importc, header: "<setjmp.h>".} @@ -241,7 +243,7 @@ proc start*(c: proc(), stacksize: int=defaultStackSize): CoroutineRef {.discarda ## Schedule coroutine for execution. It does not run immediately. if ctx == nil: initialize() - + var coro: CoroutinePtr when coroBackend == CORO_BACKEND_FIBERS: coro = cast[CoroutinePtr](alloc0(sizeof(Coroutine))) @@ -287,7 +289,7 @@ proc run*() = if current.state == CORO_FINISHED: var next = ctx.current.prev if next == nil: - # If first coroutine ends then `prev` is nil even if more coroutines + # If first coroutine ends then `prev` is nil even if more coroutines # are to be scheduled. next = ctx.current.next current.reference.coro = nil diff --git a/lib/pure/encodings.nim b/lib/pure/encodings.nim index 96f030c9b..e09b00221 100644 --- a/lib/pure/encodings.nim +++ b/lib/pure/encodings.nim @@ -213,7 +213,6 @@ when defined(windows): maxCharSize: int32 defaultChar: array[0..1, char] leadByte: array[0..12-1, char] - {.deprecated: [TCpInfo: CpInfo].} proc getCPInfo(codePage: CodePage, lpCPInfo: var CpInfo): int32 {. stdcall, importc: "GetCPInfo", dynlib: "kernel32".} diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim index dceac1763..b7498b1c5 100644 --- a/lib/pure/httpclient.nim +++ b/lib/pure/httpclient.nim @@ -848,8 +848,6 @@ proc newHttpClient*(userAgent = defUserAgent, type AsyncHttpClient* = HttpClientBase[AsyncSocket] -{.deprecated: [PAsyncHttpClient: AsyncHttpClient].} - proc newAsyncHttpClient*(userAgent = defUserAgent, maxRedirects = 5, sslContext = getDefaultSSL(), proxy: Proxy = nil): AsyncHttpClient = @@ -1163,7 +1161,7 @@ proc requestAux(client: HttpClient | AsyncHttpClient, url: string, {.multisync.} = # Helper that actually makes the request. Does not handle redirects. let requestUrl = parseUri(url) - + if requestUrl.scheme == "": raise newException(ValueError, "No uri scheme supplied.") diff --git a/lib/pure/includes/osenv.nim b/lib/pure/includes/osenv.nim index ae62a5c4e..4acc36b93 100644 --- a/lib/pure/includes/osenv.nim +++ b/lib/pure/includes/osenv.nim @@ -1,7 +1,9 @@ ## Include file that implements 'getEnv' and friends. Do not import it! -when not declared(ospaths): - {.error: "This is an include file for ospaths.nim!".} +when not declared(os): + {.error: "This is an include file for os.nim!".} + +from parseutils import skipIgnoreCase proc c_getenv(env: cstring): cstring {. importc: "getenv", header: "<stdlib.h>".} @@ -91,7 +93,10 @@ proc findEnvVar(key: string): int = getEnvVarsC() var temp = key & '=' for i in 0..high(environment): - if startsWith(environment[i], temp): return i + when defined(windows): + if skipIgnoreCase(environment[i], temp) == len(temp): return i + else: + if startsWith(environment[i], temp): return i return -1 proc getEnv*(key: string, default = ""): TaintedString {.tags: [ReadEnvEffect].} = diff --git a/lib/pure/includes/oserr.nim b/lib/pure/includes/oserr.nim index 31212d0d1..72c3f4f49 100644 --- a/lib/pure/includes/oserr.nim +++ b/lib/pure/includes/oserr.nim @@ -1,7 +1,7 @@ ## 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 declared(os): + {.error: "This is an include file for os.nim!".} when not defined(nimscript): var errno {.importc, header: "<errno.h>".}: cint diff --git a/lib/pure/math.nim b/lib/pure/math.nim index e2ad626de..ee32772b1 100644 --- a/lib/pure/math.nim +++ b/lib/pure/math.nim @@ -479,7 +479,8 @@ when not defined(JS): # C ## (-6.5 mod -2.5) == -1.5 else: # JS - proc hypot*[T: float32|float64](x, y: T): T = return sqrt(x*x + y*y) + proc hypot*(x, y: float32): float32 {.importc: "Math.hypot", varargs, nodecl.} + proc hypot*(x, y: float64): float64 {.importc: "Math.hypot", varargs, nodecl.} proc pow*(x, y: float32): float32 {.importC: "Math.pow", nodecl.} proc pow*(x, y: float64): float64 {.importc: "Math.pow", nodecl.} proc floor*(x: float32): float32 {.importc: "Math.floor", nodecl.} diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 9d74158fe..e2dd872e8 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -17,33 +17,701 @@ include "system/inclrtl" import - strutils, times + strutils -when defined(windows): - import winlean +when defined(nimscript): + discard +elif defined(windows): + import winlean, times elif defined(posix): - import posix + import posix, times proc toTime(ts: Timespec): times.Time {.inline.} = result = initTime(ts.tv_sec.int64, ts.tv_nsec.int) - else: {.error: "OS module not ported to your operating system!".} -import ospaths -export ospaths +when defined(nimscript) and defined(nimErrorProcCanHaveBody): + {.pragma: noNimScript, error: "this proc is not available on the NimScript target".} +else: + {.pragma: noNimScript.} + +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. + +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). + +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* = when defined(macosx): false else: 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 normalizePathEnd(path: var string, trailingSep = false) = + ## ensures ``path`` has exactly 0 or 1 trailing `DirSep`, depending on + ## ``trailingSep``, and taking care of edge cases: it preservers whether + ## a path is absolute or relative, and makes sure trailing sep is `DirSep`, + ## not `AltSep`. + if path.len == 0: return + var i = path.len + while i >= 1 and path[i-1] in {DirSep, AltSep}: dec(i) + if trailingSep: + # foo// => foo + path.setLen(i) + # foo => foo/ + path.add DirSep + elif i>0: + # foo// => foo + path.setLen(i) + else: + # // => / (empty case was already taken care of) + path = $DirSep + +proc normalizePathEnd(path: string, trailingSep = false): string = + result = path + result.normalizePathEnd(trailingSep) + +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.len > 0 and tail[0] in {DirSep, AltSep}: + result = head & substr(tail, 1) + else: + result = head & tail + else: + if tail.len > 0 and 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) + else: + result.head = "" + result.tail = path -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_strlen(a: cstring): cint {. - importc: "strlen", header: "<string.h>", noSideEffect.} -proc c_free(p: pointer) {. - importc: "free", header: "<stdlib.h>".} +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 the same as ``splitPath(path).head`` when ``path`` doesn't end + ## in a dir separator. + ## The remainder can be obtained with ``lastPathPart(path)`` + runnableExamples: + doAssert parentDir("") == "" + when defined(posix): + doAssert parentDir("/usr/local/bin") == "/usr/local" + doAssert parentDir("foo/bar/") == "foo" -when defined(windows): + let sepPos = parentDirPos(path) + if sepPos >= 0: + result = substr(path, 0, sepPos-1) + 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 = "" + +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: + 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)``. See also ``lastPathPart``. + runnableExamples: + when defined(posix): + doAssert extractFilename("foo/bar/") == "" + doAssert extractFilename("foo/bar") == "bar" + if path.len == 0 or path[path.len-1] in {DirSep, AltSep}: + result = "" + else: + result = splitPath(path).tail + +proc lastPathPart*(path: string): string {. + noSideEffect, rtl, extern: "nos$1".} = + ## like ``extractFilename``, but ignores trailing dir separator; aka: `baseName`:idx: + ## in some other languages. + runnableExamples: + when defined(posix): + doAssert lastPathPart("foo/bar/") == "bar" + let path = path.normalizePathEnd(trailingSep = false) + result = extractFilename(path) + +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 + runnableExamples: + when defined(macosx): + doAssert cmpPaths("foo", "Foo") == 0 + elif defined(posix): + doAssert cmpPaths("foo", "Foo") > 0 + 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. + runnableExamples: + doAssert(not "".isAbsolute) + doAssert(not ".".isAbsolute) + when defined(posix): + doAssert "/".isAbsolute + doAssert(not "a/".isAbsolute) + + if len(path) == 0: return false + + when doslikeFileSystem: + var len = len(path) + result = (path[0] in {'/', '\\'}) or + (len > 1 and path[0] in {'a'..'z', 'A'..'Z'} and path[1] == ':') + elif defined(macos): + # according to https://perldoc.perl.org/File/Spec/Mac.html `:a` is a relative path + result = path[0] != ':' + elif defined(RISCOS): + result = path[0] == '$' + elif defined(posix): + result = path[0] == '/' + +proc unixToNativePath*(path: string, drive=""): string {. + noSideEffect, rtl, extern: "nos$1".} = + ## Converts an UNIX-like path to a native one. + ## + ## 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: + if path.len == 0: + return "" + + 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 ':' + else: + result = $DirSep + start = 1 + elif path[0] == '.' and (path.len == 1 or path[1] == '/'): + # current directory + result = $CurDir + start = when doslikeFileSystem: 1 else: 2 + else: + result = "" + start = 0 + + var i = start + while i < len(path): # ../../../ --> :::: + if i+2 < path.len and 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 + else: + add result, ParDir & DirSep + inc(i, 3) + elif path[i] == '/': + add result, DirSep + inc(i) + else: + 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_HOME 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): + result = getEnv("APPDATA").string + else: + result = getEnv("XDG_CONFIG_HOME", getEnv("HOME").string / ".config").string + result.normalizePathEnd(trailingSep = true) + +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 ``~`` or a path starting with ``~/`` to a full path, replacing + ## ``~`` with ``getHomeDir()`` (otherwise returns ``path`` unmodified). + ## + ## Windows: this is still supported despite Windows platform not having this + ## convention; also, both ``~/`` and ``~\`` are handled. + runnableExamples: + doAssert expandTilde("~" / "appname.cfg") == getHomeDir() / "appname.cfg" + if len(path) == 0 or path[0] != '~': + result = path + elif len(path) == 1: + result = getHomeDir() + elif (path[1] in {DirSep, AltSep}): + result = getHomeDir() / path.substr(2) + else: + # TODO: handle `~bob` and `~bob/` which means home of bob + result = path + +# TODO: consider whether quoteShellPosix, quoteShellWindows, quoteShell, quoteShellCommand +# belong in `strutils` instead; they are not specific to paths +proc quoteShellWindows*(s: string): string {.noSideEffect, rtl, extern: "nosp$1".} = + ## Quote s, so it can be safely passed to Windows API. + ## Based on Python's subprocess.list2cmdline + ## See http://msdn.microsoft.com/en-us/library/17w5ykft.aspx + let needQuote = {' ', '\t'} in s or s.len == 0 + + result = "" + var backslashBuff = "" + if needQuote: + result.add("\"") + + for c in s: + if c == '\\': + backslashBuff.add(c) + elif c == '\"': + result.add(backslashBuff) + result.add(backslashBuff) + backslashBuff.setLen(0) + result.add("\\\"") + else: + if backslashBuff.len != 0: + result.add(backslashBuff) + backslashBuff.setLen(0) + result.add(c) + + if needQuote: + result.add("\"") + +proc quoteShellPosix*(s: string): string {.noSideEffect, rtl, extern: "nosp$1".} = + ## Quote ``s``, so it can be safely passed to POSIX shell. + ## Based on Python's pipes.quote + const safeUnixChars = {'%', '+', '-', '.', '/', '_', ':', '=', '@', + '0'..'9', 'A'..'Z', 'a'..'z'} + if s.len == 0: + return "''" + + let safe = s.allCharsInSet(safeUnixChars) + + if safe: + return s + else: + return "'" & s.replace("'", "'\"'\"'") & "'" + +when defined(windows) or defined(posix) or defined(nintendoswitch): + proc quoteShell*(s: string): string {.noSideEffect, rtl, extern: "nosp$1".} = + ## Quote ``s``, so it can be safely passed to shell. + when defined(windows): + return quoteShellWindows(s) + else: + return quoteShellPosix(s) + + proc quoteShellCommand*(args: openArray[string]): string = + ## Concatenates and quotes shell arguments `args` + runnableExamples: + when defined(posix): + assert quoteShellCommand(["aaa", "", "c d"]) == "aaa '' 'c d'" + when defined(windows): + assert quoteShellCommand(["aaa", "", "c d"]) == "aaa \"\" \"c d\"" + # can't use `map` pending https://github.com/nim-lang/Nim/issues/8303 + for i in 0..<args.len: + if i > 0: result.add " " + result.add quoteShell(args[i]) + +when not defined(nimscript): + 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_strlen(a: cstring): cint {. + importc: "strlen", header: "<string.h>", noSideEffect.} + proc c_free(p: pointer) {. + importc: "free", header: "<stdlib.h>".} + + +when defined(windows) and not defined(nimscript): when useWinUnicode: template wrapUnary(varname, winApiProc, arg: untyped) = var varname = winApiProc(newWideCString(arg)) @@ -71,9 +739,10 @@ when defined(windows): f.cFileName[1].int == dot and f.cFileName[2].int == 0) proc existsFile*(filename: string): bool {.rtl, extern: "nos$1", - tags: [ReadDirEffect].} = + tags: [ReadDirEffect], noNimScript.} = ## Returns true if `filename` exists and is a regular file or symlink. ## (directories, device files, named pipes and sockets return false) + ## This proc is not available for NimScript. when defined(windows): when useWinUnicode: wrapUnary(a, getFileAttributesW, filename) @@ -85,7 +754,8 @@ proc existsFile*(filename: string): bool {.rtl, extern: "nos$1", var res: Stat return stat(filename, res) >= 0'i32 and S_ISREG(res.st_mode) -proc existsDir*(dir: string): bool {.rtl, extern: "nos$1", tags: [ReadDirEffect].} = +proc existsDir*(dir: string): bool {.rtl, extern: "nos$1", tags: [ReadDirEffect], + noNimScript.} = ## Returns true iff the directory `dir` exists. If `dir` is a file, false ## is returned. Follows symlinks. when defined(windows): @@ -100,7 +770,8 @@ proc existsDir*(dir: string): bool {.rtl, extern: "nos$1", tags: [ReadDirEffect] return stat(dir, res) >= 0'i32 and S_ISDIR(res.st_mode) proc symlinkExists*(link: string): bool {.rtl, extern: "nos$1", - tags: [ReadDirEffect].} = + tags: [ReadDirEffect], + noNimScript.} = ## Returns true iff the symlink `link` exists. Will return true ## regardless of whether the link points to a directory or file. when defined(windows): @@ -114,15 +785,15 @@ proc symlinkExists*(link: string): bool {.rtl, extern: "nos$1", var res: Stat return lstat(link, res) >= 0'i32 and S_ISLNK(res.st_mode) -proc fileExists*(filename: string): bool {.inline.} = +proc fileExists*(filename: string): bool {.inline, noNimScript.} = ## Synonym for existsFile existsFile(filename) -proc dirExists*(dir: string): bool {.inline.} = +proc dirExists*(dir: string): bool {.inline, noNimScript.} = ## Synonym for existsDir existsDir(dir) -when not defined(windows): +when not defined(windows) and not defined(nimscript): proc checkSymlink(path: string): bool = var rawInfo: Stat if lstat(path, rawInfo) < 0'i32: result = false @@ -135,7 +806,7 @@ const proc findExe*(exe: string, followSymlinks: bool = true; extensions: openarray[string]=ExeExts): string {. - tags: [ReadDirEffect, ReadEnvEffect, ReadIOEffect].} = + tags: [ReadDirEffect, ReadEnvEffect, ReadIOEffect], noNimScript.} = ## 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` @@ -183,7 +854,11 @@ proc findExe*(exe: string, followSymlinks: bool = true; return x result = "" -proc getLastModificationTime*(file: string): times.Time {.rtl, extern: "nos$1".} = +when defined(nimscript): + const times = "fake const" + template Time(x: untyped): untyped = string + +proc getLastModificationTime*(file: string): times.Time {.rtl, extern: "nos$1", noNimScript.} = ## Returns the `file`'s last modification time. when defined(posix): var res: Stat @@ -196,7 +871,7 @@ proc getLastModificationTime*(file: string): times.Time {.rtl, extern: "nos$1".} result = fromWinTime(rdFileTime(f.ftLastWriteTime)) findClose(h) -proc getLastAccessTime*(file: string): times.Time {.rtl, extern: "nos$1".} = +proc getLastAccessTime*(file: string): times.Time {.rtl, extern: "nos$1", noNimScript.} = ## Returns the `file`'s last read or write access time. when defined(posix): var res: Stat @@ -209,7 +884,7 @@ proc getLastAccessTime*(file: string): times.Time {.rtl, extern: "nos$1".} = result = fromWinTime(rdFileTime(f.ftLastAccessTime)) findClose(h) -proc getCreationTime*(file: string): times.Time {.rtl, extern: "nos$1".} = +proc getCreationTime*(file: string): times.Time {.rtl, extern: "nos$1", noNimScript.} = ## Returns the `file`'s creation time. ## ## **Note:** Under POSIX OS's, the returned time may actually be the time at @@ -226,7 +901,7 @@ proc getCreationTime*(file: string): times.Time {.rtl, extern: "nos$1".} = result = fromWinTime(rdFileTime(f.ftCreationTime)) findClose(h) -proc fileNewer*(a, b: string): bool {.rtl, extern: "nos$1".} = +proc fileNewer*(a, b: string): bool {.rtl, extern: "nos$1", noNimScript.} = ## Returns true if the file `a` is newer than file `b`, i.e. if `a`'s ## modification time is later than `b`'s. when defined(posix): @@ -238,7 +913,7 @@ proc fileNewer*(a, b: string): bool {.rtl, extern: "nos$1".} = else: result = getLastModificationTime(a) > getLastModificationTime(b) -proc getCurrentDir*(): string {.rtl, extern: "nos$1", tags: [].} = +proc getCurrentDir*(): string {.rtl, extern: "nos$1", tags: [], noNimScript.} = ## Returns the `current working directory`:idx:. when defined(windows): var bufsize = MAX_PATH.int32 @@ -282,7 +957,7 @@ proc getCurrentDir*(): string {.rtl, extern: "nos$1", tags: [].} = else: raiseOSError(osLastError()) -proc setCurrentDir*(newDir: string) {.inline, tags: [].} = +proc setCurrentDir*(newDir: string) {.inline, tags: [], noNimScript.} = ## Sets the `current working directory`:idx:; `OSError` is raised if ## `newDir` cannot been set. when defined(Windows): @@ -294,19 +969,20 @@ proc setCurrentDir*(newDir: string) {.inline, tags: [].} = else: if chdir(newDir) != 0'i32: raiseOSError(osLastError()) -proc absolutePath*(path: string, root = getCurrentDir()): string = - ## Returns the absolute path of `path`, rooted at `root` (which must be absolute) - ## if `path` is absolute, return it, ignoring `root` - runnableExamples: - doAssert absolutePath("a") == getCurrentDir() / "a" - if isAbsolute(path): path - else: - if not root.isAbsolute: - raise newException(ValueError, "The specified root is not absolute: " & root) - joinPath(root, path) +when not defined(nimscript): + proc absolutePath*(path: string, root = getCurrentDir()): string {.noNimScript.} = + ## Returns the absolute path of `path`, rooted at `root` (which must be absolute) + ## if `path` is absolute, return it, ignoring `root` + runnableExamples: + doAssert absolutePath("a") == getCurrentDir() / "a" + if isAbsolute(path): path + else: + if not root.isAbsolute: + raise newException(ValueError, "The specified root is not absolute: " & root) + joinPath(root, path) proc expandFilename*(filename: string): string {.rtl, extern: "nos$1", - tags: [ReadDirEffect].} = + tags: [ReadDirEffect], noNimScript.} = ## Returns the full (`absolute`:idx:) path of an existing file `filename`, ## raises OSError in case of an error. Follows symlinks. when defined(windows): @@ -347,7 +1023,7 @@ proc expandFilename*(filename: string): string {.rtl, extern: "nos$1", result = $r c_free(cast[pointer](r)) -proc normalizePath*(path: var string) {.rtl, extern: "nos$1", tags: [].} = +proc normalizePath*(path: var string) {.rtl, extern: "nos$1", tags: [], noNimScript.} = ## Normalize a path. ## ## Consecutive directory separators are collapsed, including an initial double slash. @@ -383,12 +1059,12 @@ proc normalizePath*(path: var string) {.rtl, extern: "nos$1", tags: [].} = else: path = "." -proc normalizedPath*(path: string): string {.rtl, extern: "nos$1", tags: [].} = +proc normalizedPath*(path: string): string {.rtl, extern: "nos$1", tags: [], noNimScript.} = ## Returns a normalized path for the current OS. See `<#normalizePath>`_ result = path normalizePath(result) -when defined(Windows): +when defined(Windows) and not defined(nimscript): proc openHandle(path: string, followSymlink=true, writeAccess=false): Handle = var flags = FILE_FLAG_BACKUP_SEMANTICS or FILE_ATTRIBUTE_NORMAL if not followSymlink: @@ -409,7 +1085,7 @@ when defined(Windows): ) proc sameFile*(path1, path2: string): bool {.rtl, extern: "nos$1", - tags: [ReadDirEffect].} = + tags: [ReadDirEffect], noNimScript.} = ## Returns true if both pathname arguments refer to the same physical ## file or directory. Raises an exception if any of the files does not ## exist or information about it can not be obtained. @@ -449,7 +1125,7 @@ proc sameFile*(path1, path2: string): bool {.rtl, extern: "nos$1", result = a.st_dev == b.st_dev and a.st_ino == b.st_ino proc sameFileContent*(path1, path2: string): bool {.rtl, extern: "nos$1", - tags: [ReadIOEffect].} = + tags: [ReadIOEffect], noNimScript.} = ## Returns true if both pathname arguments refer to files with identical ## binary content. const @@ -492,7 +1168,7 @@ type fpOthersRead ## read access for others proc getFilePermissions*(filename: string): set[FilePermission] {. - rtl, extern: "nos$1", tags: [ReadDirEffect].} = + rtl, extern: "nos$1", tags: [ReadDirEffect], noNimScript.} = ## retrieves file permissions for `filename`. `OSError` is raised in case of ## an error. On Windows, only the ``readonly`` flag is checked, every other ## permission is available in any case. @@ -524,7 +1200,7 @@ proc getFilePermissions*(filename: string): set[FilePermission] {. result = {fpUserExec..fpOthersRead} proc setFilePermissions*(filename: string, permissions: set[FilePermission]) {. - rtl, extern: "nos$1", tags: [WriteDirEffect].} = + rtl, extern: "nos$1", tags: [WriteDirEffect], noNimScript.} = ## sets the file permissions for `filename`. `OSError` is raised in case of ## an error. On Windows, only the ``readonly`` flag is changed, depending on ## ``fpUserWrite``. @@ -560,7 +1236,7 @@ proc setFilePermissions*(filename: string, permissions: set[FilePermission]) {. if res2 == - 1'i32: raiseOSError(osLastError()) proc copyFile*(source, dest: string) {.rtl, extern: "nos$1", - tags: [ReadIOEffect, WriteIOEffect].} = + tags: [ReadIOEffect, WriteIOEffect], noNimScript.} = ## Copies a file from `source` to `dest`. ## ## If this fails, `OSError` is raised. On the Windows platform this proc will @@ -611,7 +1287,7 @@ when not declared(ENOENT) and not defined(Windows): else: var ENOENT {.importc, header: "<errno.h>".}: cint -when defined(Windows): +when defined(Windows) and not defined(nimscript): when useWinUnicode: template deleteFile(file: untyped): untyped = deleteFileW(file) template setFileAttributes(file, attrs: untyped): untyped = @@ -621,7 +1297,7 @@ when defined(Windows): template setFileAttributes(file, attrs: untyped): untyped = setFileAttributesA(file, attrs) -proc tryRemoveFile*(file: string): bool {.rtl, extern: "nos$1", tags: [WriteDirEffect].} = +proc tryRemoveFile*(file: string): bool {.rtl, extern: "nos$1", tags: [WriteDirEffect], noNimScript.} = ## Removes the `file`. If this fails, returns `false`. This does not fail ## if the file never existed in the first place. ## On Windows, ignores the read-only attribute. @@ -644,7 +1320,7 @@ proc tryRemoveFile*(file: string): bool {.rtl, extern: "nos$1", tags: [WriteDirE if unlink(file) != 0'i32 and errno != ENOENT: result = false -proc removeFile*(file: string) {.rtl, extern: "nos$1", tags: [WriteDirEffect].} = +proc removeFile*(file: string) {.rtl, extern: "nos$1", tags: [WriteDirEffect], noNimScript.} = ## Removes the `file`. If this fails, `OSError` is raised. This does not fail ## if the file never existed in the first place. ## On Windows, ignores the read-only attribute. @@ -654,7 +1330,7 @@ proc removeFile*(file: string) {.rtl, extern: "nos$1", tags: [WriteDirEffect].} else: raiseOSError(osLastError(), $strerror(errno)) -proc tryMoveFSObject(source, dest: string): bool = +proc tryMoveFSObject(source, dest: string): bool {.noNimScript.} = ## Moves a file or directory from `source` to `dest`. Returns false in case ## of `EXDEV` error. In case of other errors `OSError` is raised. Returns ## true in case of success. @@ -675,7 +1351,7 @@ proc tryMoveFSObject(source, dest: string): bool = return true proc moveFile*(source, dest: string) {.rtl, extern: "nos$1", - tags: [ReadIOEffect, WriteIOEffect].} = + tags: [ReadIOEffect, WriteIOEffect], noNimScript.} = ## Moves a file from `source` to `dest`. If this fails, `OSError` is raised. ## Can be used to `rename files`:idx: if not tryMoveFSObject(source, dest): @@ -689,7 +1365,7 @@ proc moveFile*(source, dest: string) {.rtl, extern: "nos$1", raise proc execShellCmd*(command: string): int {.rtl, extern: "nos$1", - tags: [ExecIOEffect].} = + tags: [ExecIOEffect], noNimScript.} = ## Executes a `shell command`:idx:. ## ## Command has the form 'program args' where args are the command @@ -704,7 +1380,7 @@ proc execShellCmd*(command: string): int {.rtl, extern: "nos$1", result = c_system(command) # Templates for filtering directories and files -when defined(windows): +when defined(windows) and not defined(nimscript): template isDir(f: WIN32_FIND_DATA): bool = (f.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) != 0'i32 template isFile(f: WIN32_FIND_DATA): bool = @@ -760,7 +1436,7 @@ template walkCommon(pattern: string, filter) = if filter(path): yield path -iterator walkPattern*(pattern: string): string {.tags: [ReadDirEffect].} = +iterator walkPattern*(pattern: string): string {.tags: [ReadDirEffect], noNimScript.} = ## Iterate over all the files and directories that match the `pattern`. ## On POSIX this uses the `glob`:idx: call. ## @@ -768,7 +1444,7 @@ iterator walkPattern*(pattern: string): string {.tags: [ReadDirEffect].} = ## notation is supported. walkCommon(pattern, defaultWalkFilter) -iterator walkFiles*(pattern: string): string {.tags: [ReadDirEffect].} = +iterator walkFiles*(pattern: string): string {.tags: [ReadDirEffect], noNimScript.} = ## Iterate over all the files that match the `pattern`. On POSIX this uses ## the `glob`:idx: call. ## @@ -776,7 +1452,7 @@ iterator walkFiles*(pattern: string): string {.tags: [ReadDirEffect].} = ## notation is supported. walkCommon(pattern, isFile) -iterator walkDirs*(pattern: string): string {.tags: [ReadDirEffect].} = +iterator walkDirs*(pattern: string): string {.tags: [ReadDirEffect], noNimScript.} = ## Iterate over all the directories that match the `pattern`. ## On POSIX this uses the `glob`:idx: call. ## @@ -791,7 +1467,7 @@ type pcDir, ## path refers to a directory pcLinkToDir ## path refers to a symbolic link to a directory -when defined(posix): +when defined(posix) and not defined(nimscript): proc getSymlinkFileKind(path: string): PathComponent = # Helper function. var s: Stat @@ -832,7 +1508,10 @@ iterator walkDir*(dir: string; relative=false): tuple[kind: PathComponent, path: for k, v in items(staticWalkDir(dir, relative)): yield (k, v) else: - when defined(windows): + when defined(nimscript): + for k, v in items(staticWalkDir(dir, relative)): + yield (k, v) + elif defined(windows): var f: WIN32_FIND_DATA var h = findFirstFile(dir / "*", f) if h != -1: @@ -920,7 +1599,7 @@ iterator walkDirRec*(dir: string, yieldFilter = {pcFile}, if k in yieldFilter: yield p -proc rawRemoveDir(dir: string) = +proc rawRemoveDir(dir: string) {.noNimScript.} = when defined(windows): when useWinUnicode: wrapUnary(res, removeDirectoryW, dir) @@ -934,7 +1613,7 @@ proc rawRemoveDir(dir: string) = if rmdir(dir) != 0'i32 and errno != ENOENT: raiseOSError(osLastError()) proc removeDir*(dir: string) {.rtl, extern: "nos$1", tags: [ - WriteDirEffect, ReadDirEffect], benign.} = + WriteDirEffect, ReadDirEffect], benign, noNimScript.} = ## Removes the directory `dir` including all subdirectories and files ## in `dir` (recursively). ## @@ -946,7 +1625,7 @@ proc removeDir*(dir: string) {.rtl, extern: "nos$1", tags: [ of pcDir: removeDir(path) rawRemoveDir(dir) -proc rawCreateDir(dir: string): bool = +proc rawCreateDir(dir: string): bool {.noNimScript.} = # Try to create one directory (not the whole path). # returns `true` for success, `false` if the path has previously existed # @@ -991,7 +1670,7 @@ proc rawCreateDir(dir: string): bool = raiseOSError(osLastError(), dir) proc existsOrCreateDir*(dir: string): bool {.rtl, extern: "nos$1", - tags: [WriteDirEffect, ReadDirEffect].} = + tags: [WriteDirEffect, ReadDirEffect], noNimScript.} = ## Check if a `directory`:idx: `dir` exists, and create it otherwise. ## ## Does not create parent directories (fails if parent does not exist). @@ -1004,7 +1683,7 @@ proc existsOrCreateDir*(dir: string): bool {.rtl, extern: "nos$1", raise newException(IOError, "Failed to create '" & dir & "'") proc createDir*(dir: string) {.rtl, extern: "nos$1", - tags: [WriteDirEffect, ReadDirEffect].} = + tags: [WriteDirEffect, ReadDirEffect], noNimScript.} = ## Creates the `directory`:idx: `dir`. ## ## The directory may contain several subdirectories that do not exist yet. @@ -1027,7 +1706,7 @@ proc createDir*(dir: string) {.rtl, extern: "nos$1", discard existsOrCreateDir(dir) proc copyDir*(source, dest: string) {.rtl, extern: "nos$1", - tags: [WriteIOEffect, ReadIOEffect], benign.} = + tags: [WriteIOEffect, ReadIOEffect], benign, noNimScript.} = ## Copies a directory from `source` to `dest`. ## ## If this fails, `OSError` is raised. On the Windows platform this proc will @@ -1045,7 +1724,7 @@ proc copyDir*(source, dest: string) {.rtl, extern: "nos$1", copyDir(path, dest / noSource) else: discard -proc createSymlink*(src, dest: string) = +proc createSymlink*(src, dest: string) {.noNimScript.} = ## Create a symbolic link at `dest` which points to the item specified ## by `src`. On most operating systems, will fail if a link already exists. ## @@ -1068,7 +1747,7 @@ proc createSymlink*(src, dest: string) = if symlink(src, dest) != 0: raiseOSError(osLastError()) -proc createHardlink*(src, dest: string) = +proc createHardlink*(src, dest: string) {.noNimScript.} = ## Create a hard link at `dest` which points to the item specified ## by `src`. ## @@ -1176,7 +1855,7 @@ proc parseCmdLine*(c: string): seq[string] {. add(result, a) proc copyFileWithPermissions*(source, dest: string, - ignorePermissionErrors = true) = + ignorePermissionErrors = true) {.noNimScript.} = ## Copies a file from `source` to `dest` preserving file permissions. ## ## This is a wrapper proc around `copyFile() <#copyFile>`_, @@ -1200,7 +1879,7 @@ proc copyFileWithPermissions*(source, dest: string, proc copyDirWithPermissions*(source, dest: string, ignorePermissionErrors = true) {.rtl, extern: "nos$1", - tags: [WriteIOEffect, ReadIOEffect], benign.} = + tags: [WriteIOEffect, ReadIOEffect], benign, noNimScript.} = ## Copies a directory from `source` to `dest` preserving file permissions. ## ## If this fails, `OSError` is raised. This is a wrapper proc around `copyDir() @@ -1231,7 +1910,7 @@ proc copyDirWithPermissions*(source, dest: string, proc inclFilePermissions*(filename: string, permissions: set[FilePermission]) {. - rtl, extern: "nos$1", tags: [ReadDirEffect, WriteDirEffect].} = + rtl, extern: "nos$1", tags: [ReadDirEffect, WriteDirEffect], noNimScript.} = ## a convenience procedure for: ## ## .. code-block:: nim @@ -1240,14 +1919,14 @@ proc inclFilePermissions*(filename: string, proc exclFilePermissions*(filename: string, permissions: set[FilePermission]) {. - rtl, extern: "nos$1", tags: [ReadDirEffect, WriteDirEffect].} = + rtl, extern: "nos$1", tags: [ReadDirEffect, WriteDirEffect], noNimScript.} = ## a convenience procedure for: ## ## .. code-block:: nim ## setFilePermissions(filename, getFilePermissions(filename)-permissions) setFilePermissions(filename, getFilePermissions(filename)-permissions) -proc moveDir*(source, dest: string) {.tags: [ReadIOEffect, WriteIOEffect].} = +proc moveDir*(source, dest: string) {.tags: [ReadIOEffect, WriteIOEffect], noNimScript.} = ## Moves a directory from `source` to `dest`. If this fails, `OSError` is raised. if not tryMoveFSObject(source, dest): when not defined(windows): @@ -1255,9 +1934,7 @@ proc moveDir*(source, dest: string) {.tags: [ReadIOEffect, WriteIOEffect].} = copyDir(source, dest) removeDir(source) -#include ospaths - -proc expandSymlink*(symlinkPath: string): string = +proc expandSymlink*(symlinkPath: string): string {.noNimScript.} = ## Returns a string representing the path to which the symbolic link points. ## ## On Windows this is a noop, ``symlinkPath`` is simply returned. @@ -1320,6 +1997,13 @@ when defined(nimdoc): ## else: ## # Do something else! +elif defined(nintendoswitch) or defined(nimscript): + proc paramStr*(i: int): TaintedString {.tags: [ReadIOEffect].} = + raise newException(OSError, "paramStr is not implemented on Nintendo Switch") + + proc paramCount*(): int {.tags: [ReadIOEffect].} = + raise newException(OSError, "paramCount is not implemented on Nintendo Switch") + elif defined(windows): # Since we support GUI applications with Nim, we sometimes generate # a WinMain entry proc. But a WinMain proc has no access to the parsed @@ -1346,13 +2030,6 @@ elif defined(windows): if i < ownArgv.len and i >= 0: return TaintedString(ownArgv[i]) raise newException(IndexError, "invalid index") -elif defined(nintendoswitch): - proc paramStr*(i: int): TaintedString {.tags: [ReadIOEffect].} = - raise newException(OSError, "paramStr is not implemented on Nintendo Switch") - - proc paramCount*(): int {.tags: [ReadIOEffect].} = - raise newException(OSError, "paramCount is not implemented on Nintendo Switch") - elif defined(genode): proc paramStr*(i: int): TaintedString = raise newException(OSError, "paramStr is not implemented on Genode") @@ -1397,7 +2074,7 @@ when declared(paramCount) or defined(nimdoc): for i in 1..paramCount(): result.add(paramStr(i)) -when defined(freebsd) or defined(dragonfly): +when not defined(nimscript) and (defined(freebsd) or defined(dragonfly)): proc sysctl(name: ptr cint, namelen: cuint, oldp: pointer, oldplen: ptr csize, newp: pointer, newplen: csize): cint {.importc: "sysctl",header: """#include <sys/types.h> @@ -1430,7 +2107,7 @@ when defined(freebsd) or defined(dragonfly): result.setLen(pathLength) break -when defined(linux) or defined(solaris) or defined(bsd) or defined(aix): +when not defined(nimscript) and (defined(linux) or defined(solaris) or defined(bsd) or defined(aix)): proc getApplAux(procPath: string): string = result = newString(256) var len = readlink(procPath, result, 256) @@ -1439,7 +2116,7 @@ when defined(linux) or defined(solaris) or defined(bsd) or defined(aix): len = readlink(procPath, result, len) setLen(result, len) -when not (defined(windows) or defined(macosx)): +when not (defined(windows) or defined(macosx) or defined(nimscript)): proc getApplHeuristic(): string = when declared(paramStr): result = string(paramStr(0)) @@ -1483,7 +2160,7 @@ when defined(haiku): else: result = "" -proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect].} = +proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect], noNimScript.} = ## Returns the filename of the application's executable. ## ## This procedure will resolve symlinks. @@ -1543,11 +2220,11 @@ proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect].} = if result.len == 0: result = getApplHeuristic() -proc getAppDir*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect].} = +proc getAppDir*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect], noNimScript.} = ## Returns the directory of the application's executable. result = splitFile(getAppFilename()).dir -proc sleep*(milsecs: int) {.rtl, extern: "nos$1", tags: [TimeEffect].} = +proc sleep*(milsecs: int) {.rtl, extern: "nos$1", tags: [TimeEffect], noNimScript.} = ## sleeps `milsecs` milliseconds. when defined(windows): winlean.sleep(int32(milsecs)) @@ -1558,7 +2235,7 @@ proc sleep*(milsecs: int) {.rtl, extern: "nos$1", tags: [TimeEffect].} = discard posix.nanosleep(a, b) proc getFileSize*(file: string): BiggestInt {.rtl, extern: "nos$1", - tags: [ReadIOEffect].} = + tags: [ReadIOEffect], noNimScript.} = ## returns the file size of `file` (in bytes). An ``OSError`` exception is ## raised in case of an error. when defined(windows): @@ -1574,7 +2251,7 @@ proc getFileSize*(file: string): BiggestInt {.rtl, extern: "nos$1", close(f) else: raiseOSError(osLastError()) -when defined(Windows): +when defined(Windows) or defined(nimscript): type DeviceId* = int32 FileId* = int64 @@ -1654,7 +2331,7 @@ template rawToFormalFileInfo(rawInfo, path, formalInfo): untyped = assert(path != "") # symlinks can't occur for file handles formalInfo.kind = getSymlinkFileKind(path) -proc getFileInfo*(handle: FileHandle): FileInfo = +proc getFileInfo*(handle: FileHandle): FileInfo {.noNimScript.} = ## Retrieves file information for the file object represented by the given ## handle. ## @@ -1675,12 +2352,12 @@ proc getFileInfo*(handle: FileHandle): FileInfo = raiseOSError(osLastError()) rawToFormalFileInfo(rawInfo, "", result) -proc getFileInfo*(file: File): FileInfo = +proc getFileInfo*(file: File): FileInfo {.noNimScript.} = if file.isNil: raise newException(IOError, "File is nil") result = getFileInfo(file.getFileHandle()) -proc getFileInfo*(path: string, followSymlink = true): FileInfo = +proc getFileInfo*(path: string, followSymlink = true): FileInfo {.noNimScript.} = ## Retrieves file information for the file object pointed to by `path`. ## ## Due to intrinsic differences between operating systems, the information @@ -1714,7 +2391,7 @@ proc getFileInfo*(path: string, followSymlink = true): FileInfo = raiseOSError(osLastError()) rawToFormalFileInfo(rawInfo, path, result) -proc isHidden*(path: string): bool = +proc isHidden*(path: string): bool {.noNimScript.} = ## Determines whether ``path`` is hidden or not, using this ## reference https://en.wikipedia.org/wiki/Hidden_file_and_hidden_directory ## @@ -1744,7 +2421,7 @@ proc isHidden*(path: string): bool = {.pop.} -proc setLastModificationTime*(file: string, t: times.Time) = +proc setLastModificationTime*(file: string, t: times.Time) {.noNimScript.} = ## Sets the `file`'s last modification time. `OSError` is raised in case of ## an error. when defined(posix): @@ -1760,3 +2437,37 @@ proc setLastModificationTime*(file: string, t: times.Time) = let res = setFileTime(h, nil, nil, ft.addr) discard h.closeHandle if res == 0'i32: raiseOSError(osLastError()) + +when isMainModule: + assert quoteShellWindows("aaa") == "aaa" + assert quoteShellWindows("aaa\"") == "aaa\\\"" + assert quoteShellWindows("") == "\"\"" + + assert quoteShellPosix("aaa") == "aaa" + assert quoteShellPosix("aaa a") == "'aaa a'" + assert quoteShellPosix("") == "''" + assert quoteShellPosix("a'a") == "'a'\"'\"'a'" + + when defined(posix): + assert quoteShell("") == "''" + + block normalizePathEndTest: + # handle edge cases correctly: shouldn't affect whether path is + # absolute/relative + doAssert "".normalizePathEnd(true) == "" + doAssert "".normalizePathEnd(false) == "" + doAssert "/".normalizePathEnd(true) == $DirSep + doAssert "/".normalizePathEnd(false) == $DirSep + + when defined(posix): + doAssert "//".normalizePathEnd(false) == "/" + doAssert "foo.bar//".normalizePathEnd == "foo.bar" + doAssert "bar//".normalizePathEnd(trailingSep = true) == "bar/" + when defined(Windows): + doAssert r"C:\foo\\".normalizePathEnd == r"C:\foo" + doAssert r"C:\foo".normalizePathEnd(trailingSep = true) == r"C:\foo\" + # this one is controversial: we could argue for returning `D:\` instead, + # but this is simplest. + doAssert r"D:\".normalizePathEnd == r"D:" + doAssert r"E:/".normalizePathEnd(trailingSep = true) == r"E:\" + doAssert "/".normalizePathEnd == r"\" diff --git a/lib/pure/ospaths.nim b/lib/pure/ospaths.nim deleted file mode 100644 index 7f7f9a425..000000000 --- a/lib/pure/ospaths.nim +++ /dev/null @@ -1,713 +0,0 @@ -# -# -# Nim's Runtime Library -# (c) Copyright 2015 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -# Forwarded by the ``os`` module but a module in its own right for NimScript -# support. - -include "system/inclrtl" - -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. - -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). - -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* = when defined(macosx): false else: 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 normalizePathEnd(path: var string, trailingSep = false) = - ## ensures ``path`` has exactly 0 or 1 trailing `DirSep`, depending on - ## ``trailingSep``, and taking care of edge cases: it preservers whether - ## a path is absolute or relative, and makes sure trailing sep is `DirSep`, - ## not `AltSep`. - if path.len == 0: return - var i = path.len - while i >= 1 and path[i-1] in {DirSep, AltSep}: dec(i) - if trailingSep: - # foo// => foo - path.setLen(i) - # foo => foo/ - path.add DirSep - elif i>0: - # foo// => foo - path.setLen(i) - else: - # // => / (empty case was already taken care of) - path = $DirSep - -proc normalizePathEnd(path: string, trailingSep = false): string = - result = path - result.normalizePathEnd(trailingSep) - -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.len > 0 and tail[0] in {DirSep, AltSep}: - result = head & substr(tail, 1) - else: - result = head & tail - else: - if tail.len > 0 and 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) - 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 the same as ``splitPath(path).head`` when ``path`` doesn't end - ## in a dir separator. - ## The remainder can be obtained with ``lastPathPart(path)`` - runnableExamples: - doAssert parentDir("") == "" - when defined(posix): - doAssert parentDir("/usr/local/bin") == "/usr/local" - doAssert parentDir("foo/bar/") == "foo" - - let sepPos = parentDirPos(path) - if sepPos >= 0: - result = substr(path, 0, sepPos-1) - 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 = "" - -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: - 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)``. See also ``lastPathPart``. - runnableExamples: - when defined(posix): - doAssert extractFilename("foo/bar/") == "" - doAssert extractFilename("foo/bar") == "bar" - if path.len == 0 or path[path.len-1] in {DirSep, AltSep}: - result = "" - else: - result = splitPath(path).tail - -proc lastPathPart*(path: string): string {. - noSideEffect, rtl, extern: "nos$1".} = - ## like ``extractFilename``, but ignores trailing dir separator; aka: `baseName`:idx: - ## in some other languages. - runnableExamples: - when defined(posix): - doAssert lastPathPart("foo/bar/") == "bar" - let path = path.normalizePathEnd(trailingSep = false) - result = extractFilename(path) - -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 - runnableExamples: - when defined(macosx): - doAssert cmpPaths("foo", "Foo") == 0 - elif defined(posix): - doAssert cmpPaths("foo", "Foo") > 0 - 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. - runnableExamples: - doAssert(not "".isAbsolute) - doAssert(not ".".isAbsolute) - when defined(posix): - doAssert "/".isAbsolute - doAssert(not "a/".isAbsolute) - - if len(path) == 0: return false - - when doslikeFileSystem: - var len = len(path) - result = (path[0] in {'/', '\\'}) or - (len > 1 and path[0] in {'a'..'z', 'A'..'Z'} and path[1] == ':') - elif defined(macos): - # according to https://perldoc.perl.org/File/Spec/Mac.html `:a` is a relative path - result = path[0] != ':' - elif defined(RISCOS): - result = path[0] == '$' - elif defined(posix): - result = path[0] == '/' - -proc unixToNativePath*(path: string, drive=""): string {. - noSideEffect, rtl, extern: "nos$1".} = - ## Converts an UNIX-like path to a native one. - ## - ## 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: - if path.len == 0: - return "" - - 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 ':' - else: - result = $DirSep - start = 1 - elif path[0] == '.' and (path.len == 1 or path[1] == '/'): - # current directory - result = $CurDir - start = when doslikeFileSystem: 1 else: 2 - else: - result = "" - start = 0 - - var i = start - while i < len(path): # ../../../ --> :::: - if i+2 < path.len and 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 - else: - add result, ParDir & DirSep - inc(i, 3) - elif path[i] == '/': - add result, DirSep - inc(i) - else: - 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_HOME 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): - result = getEnv("APPDATA").string - else: - result = getEnv("XDG_CONFIG_HOME", getEnv("HOME").string / ".config").string - result.normalizePathEnd(trailingSep = true) - -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 ``~`` or a path starting with ``~/`` to a full path, replacing - ## ``~`` with ``getHomeDir()`` (otherwise returns ``path`` unmodified). - ## - ## Windows: this is still supported despite Windows platform not having this - ## convention; also, both ``~/`` and ``~\`` are handled. - runnableExamples: - doAssert expandTilde("~" / "appname.cfg") == getHomeDir() / "appname.cfg" - if len(path) == 0 or path[0] != '~': - result = path - elif len(path) == 1: - result = getHomeDir() - elif (path[1] in {DirSep, AltSep}): - result = getHomeDir() / path.substr(2) - else: - # TODO: handle `~bob` and `~bob/` which means home of bob - result = path - -# TODO: consider whether quoteShellPosix, quoteShellWindows, quoteShell, quoteShellCommand -# belong in `strutils` instead; they are not specific to paths -proc quoteShellWindows*(s: string): string {.noSideEffect, rtl, extern: "nosp$1".} = - ## Quote s, so it can be safely passed to Windows API. - ## Based on Python's subprocess.list2cmdline - ## See http://msdn.microsoft.com/en-us/library/17w5ykft.aspx - let needQuote = {' ', '\t'} in s or s.len == 0 - - result = "" - var backslashBuff = "" - if needQuote: - result.add("\"") - - for c in s: - if c == '\\': - backslashBuff.add(c) - elif c == '\"': - result.add(backslashBuff) - result.add(backslashBuff) - backslashBuff.setLen(0) - result.add("\\\"") - else: - if backslashBuff.len != 0: - result.add(backslashBuff) - backslashBuff.setLen(0) - result.add(c) - - if needQuote: - result.add("\"") - -proc quoteShellPosix*(s: string): string {.noSideEffect, rtl, extern: "nosp$1".} = - ## Quote ``s``, so it can be safely passed to POSIX shell. - ## Based on Python's pipes.quote - const safeUnixChars = {'%', '+', '-', '.', '/', '_', ':', '=', '@', - '0'..'9', 'A'..'Z', 'a'..'z'} - if s.len == 0: - return "''" - - let safe = s.allCharsInSet(safeUnixChars) - - if safe: - return s - else: - return "'" & s.replace("'", "'\"'\"'") & "'" - -when defined(windows) or defined(posix) or defined(nintendoswitch): - proc quoteShell*(s: string): string {.noSideEffect, rtl, extern: "nosp$1".} = - ## Quote ``s``, so it can be safely passed to shell. - when defined(windows): - return quoteShellWindows(s) - else: - return quoteShellPosix(s) - - proc quoteShellCommand*(args: openArray[string]): string = - ## Concatenates and quotes shell arguments `args` - runnableExamples: - when defined(posix): - assert quoteShellCommand(["aaa", "", "c d"]) == "aaa '' 'c d'" - when defined(windows): - assert quoteShellCommand(["aaa", "", "c d"]) == "aaa \"\" \"c d\"" - # can't use `map` pending https://github.com/nim-lang/Nim/issues/8303 - for i in 0..<args.len: - if i > 0: result.add " " - result.add quoteShell(args[i]) - -when isMainModule: - assert quoteShellWindows("aaa") == "aaa" - assert quoteShellWindows("aaa\"") == "aaa\\\"" - assert quoteShellWindows("") == "\"\"" - - assert quoteShellPosix("aaa") == "aaa" - assert quoteShellPosix("aaa a") == "'aaa a'" - assert quoteShellPosix("") == "''" - assert quoteShellPosix("a'a") == "'a'\"'\"'a'" - - when defined(posix): - assert quoteShell("") == "''" - - block normalizePathEndTest: - # handle edge cases correctly: shouldn't affect whether path is - # absolute/relative - doAssert "".normalizePathEnd(true) == "" - doAssert "".normalizePathEnd(false) == "" - doAssert "/".normalizePathEnd(true) == $DirSep - doAssert "/".normalizePathEnd(false) == $DirSep - - when defined(posix): - doAssert "//".normalizePathEnd(false) == "/" - doAssert "foo.bar//".normalizePathEnd == "foo.bar" - doAssert "bar//".normalizePathEnd(trailingSep = true) == "bar/" - when defined(Windows): - doAssert r"C:\foo\\".normalizePathEnd == r"C:\foo" - doAssert r"C:\foo".normalizePathEnd(trailingSep = true) == r"C:\foo\" - # this one is controversial: we could argue for returning `D:\` instead, - # but this is simplest. - doAssert r"D:\".normalizePathEnd == r"D:" - doAssert r"E:/".normalizePathEnd(trailingSep = true) == r"E:\" - doAssert "/".normalizePathEnd == r"\" diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index dfea4213d..ac455ce99 100644 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -15,7 +15,6 @@ include "system/inclrtl" import strutils, os, strtabs, streams, cpuinfo -from ospaths import quoteShell, quoteShellWindows, quoteShellPosix export quoteShell, quoteShellWindows, quoteShellPosix when defined(windows): @@ -1324,6 +1323,12 @@ proc execCmdEx*(command: string, options: set[ProcessOption] = { ## let (outp, errC) = execCmdEx("nim c -r mytestfile.nim") var p = startProcess(command, options=options + {poEvalCommand}) var outp = outputStream(p) + + # There is no way to provide input for the child process + # anymore. Closing it will create EOF on stdin instead of eternal + # blocking. + close inputStream(p) + result = (TaintedString"", -1) var line = newStringOfCap(120).TaintedString while true: diff --git a/lib/pure/parseopt.nim b/lib/pure/parseopt.nim index c91134738..fe3d3186f 100644 --- a/lib/pure/parseopt.nim +++ b/lib/pure/parseopt.nim @@ -44,9 +44,9 @@ type cmdShortOption ## a short option ``-c`` detected OptParser* = object of RootObj ## this object implements the command line parser - cmd*: string # cmd,pos exported so caller can catch "--" as.. pos*: int # ..empty key or subcmd cmdArg & handle specially inShortState: bool + allowWhitespaceAfterColon: bool shortNoVal: set[char] longNoVal: seq[string] cmds: seq[string] @@ -95,7 +95,8 @@ when declared(os.paramCount): # access the command line arguments then! proc initOptParser*(cmdline = "", shortNoVal: set[char]={}, - longNoVal: seq[string] = @[]): OptParser = + longNoVal: seq[string] = @[]; + allowWhitespaceAfterColon = true): OptParser = ## inits the option parser. If ``cmdline == ""``, the real command line ## (as provided by the ``OS`` module) is taken. If ``shortNoVal`` is ## provided command users do not need to delimit short option keys and @@ -108,23 +109,21 @@ when declared(os.paramCount): result.inShortState = false result.shortNoVal = shortNoVal result.longNoVal = longNoVal + result.allowWhitespaceAfterColon = allowWhitespaceAfterColon if cmdline != "": - result.cmd = cmdline result.cmds = parseCmdLine(cmdline) else: - result.cmd = "" result.cmds = newSeq[string](paramCount()) for i in countup(1, paramCount()): result.cmds[i-1] = paramStr(i).string - result.cmd.add quote(result.cmds[i-1]) - result.cmd.add ' ' result.kind = cmdEnd result.key = TaintedString"" result.val = TaintedString"" proc initOptParser*(cmdline: seq[TaintedString], shortNoVal: set[char]={}, - longNoVal: seq[string] = @[]): OptParser = + longNoVal: seq[string] = @[]; + allowWhitespaceAfterColon = true): OptParser = ## inits the option parser. If ``cmdline.len == 0``, the real command line ## (as provided by the ``OS`` module) is taken. ``shortNoVal`` and ## ``longNoVal`` behavior is the same as for ``initOptParser(string,...)``. @@ -133,19 +132,15 @@ when declared(os.paramCount): result.inShortState = false result.shortNoVal = shortNoVal result.longNoVal = longNoVal - result.cmd = "" + result.allowWhitespaceAfterColon = allowWhitespaceAfterColon if cmdline.len != 0: result.cmds = newSeq[string](cmdline.len) for i in 0..<cmdline.len: result.cmds[i] = cmdline[i].string - result.cmd.add quote(cmdline[i].string) - result.cmd.add ' ' else: result.cmds = newSeq[string](paramCount()) for i in countup(1, paramCount()): result.cmds[i-1] = paramStr(i).string - result.cmd.add quote(result.cmds[i-1]) - result.cmd.add ' ' result.kind = cmdEnd result.key = TaintedString"" result.val = TaintedString"" @@ -210,7 +205,7 @@ proc next*(p: var OptParser) {.rtl, extern: "npo$1".} = inc(i) while i < p.cmds[p.idx].len and p.cmds[p.idx][i] in {'\t', ' '}: inc(i) # if we're at the end, use the next command line option: - if i >= p.cmds[p.idx].len and p.idx < p.cmds.len: + if i >= p.cmds[p.idx].len and p.idx < p.cmds.len and p.allowWhitespaceAfterColon: inc p.idx i = 0 p.val = TaintedString p.cmds[p.idx].substr(i) diff --git a/lib/pure/parsesql.nim b/lib/pure/parsesql.nim index 4b841b9e1..20f02e815 100644 --- a/lib/pure/parsesql.nim +++ b/lib/pure/parsesql.nim @@ -566,9 +566,6 @@ type SqlParser* = object of SqlLexer ## SQL parser object tok: Token -{.deprecated: [EInvalidSql: SqlParseError, PSqlNode: SqlNode, - TSqlNode: SqlNodeObj, TSqlParser: SqlParser, TSqlNodeKind: SqlNodeKind].} - proc newNode*(k: SqlNodeKind): SqlNode = new(result) result.kind = k diff --git a/lib/pure/pegs.nim b/lib/pure/pegs.nim index 3909d18f8..957091918 100644 --- a/lib/pure/pegs.nim +++ b/lib/pure/pegs.nim @@ -90,19 +90,19 @@ proc kind*(p: Peg): PegKind = p.kind ## Returns the *PegKind* of a given *Peg* object. proc term*(p: Peg): string = p.term - ## Returns the *string* representation of a given *Peg* variant object + ## Returns the *string* representation of a given *Peg* variant object ## where present. proc ch*(p: Peg): char = p.ch - ## Returns the *char* representation of a given *Peg* variant object + ## Returns the *char* representation of a given *Peg* variant object ## where present. proc charChoice*(p: Peg): ref set[char] = p.charChoice - ## Returns the *charChoice* field of a given *Peg* variant object + ## Returns the *charChoice* field of a given *Peg* variant object ## where present. proc nt*(p: Peg): NonTerminal = p.nt - ## Returns the *NonTerminal* object of a given *Peg* variant object + ## Returns the *NonTerminal* object of a given *Peg* variant object ## where present. proc index*(p: Peg): range[0..MaxSubpatterns] = p.index @@ -137,7 +137,7 @@ proc flags*(nt: NonTerminal): set[NonTerminalFlag] = nt.flags proc rule*(nt: NonTerminal): Peg = nt.rule ## Gets the *Peg* object representing the rule definition of the parent *Peg* - ## object variant of a given *NonTerminal*. + ## object variant of a given *NonTerminal*. proc term*(t: string): Peg {.nosideEffect, rtl, extern: "npegs$1Str".} = ## constructs a PEG from a terminal string @@ -553,8 +553,6 @@ type ml: int origStart: int -{.deprecated: [TCaptures: Captures].} - proc bounds*(c: Captures, i: range[0..MaxSubpatterns-1]): tuple[first, last: int] = ## returns the bounds ``[first..last]`` of the `i`'th capture. @@ -885,7 +883,7 @@ proc rawMatch*(s: string, p: Peg, start: int, c: var Captures): int macro mkHandlerTplts(handlers: untyped): untyped = # Transforms the handler spec in *handlers* into handler templates. # The AST structure of *handlers[0]*: - # + # # .. code-block:: # StmtList # Call @@ -1009,7 +1007,7 @@ template eventParser*(pegAst, handlers: untyped): (proc(s: string): int) = ## echo opStack ## ## let pLen = parseArithExpr(txt) - ## + ## ## The *handlers* parameter consists of code blocks for *PegKinds*, ## which define the grammar elements of interest. Each block can contain ## handler code to be executed when the parser enters and leaves text diff --git a/lib/pure/random.nim b/lib/pure/random.nim index a2c2c1f88..c458d51eb 100644 --- a/lib/pure/random.nim +++ b/lib/pure/random.nim @@ -231,4 +231,8 @@ when isMainModule: except RangeError: discard + + # don't use causes integer overflow + doAssert compiles(random[int](low(int) .. high(int))) + main() diff --git a/lib/pure/smtp.nim b/lib/pure/smtp.nim index d9b863a52..5f4b09f80 100644 --- a/lib/pure/smtp.nim +++ b/lib/pure/smtp.nim @@ -71,7 +71,12 @@ when not defined(ssl): type PSSLContext = ref object let defaultSSLContext: PSSLContext = nil else: - let defaultSSLContext = newContext(verifyMode = CVerifyNone) + var defaultSSLContext {.threadvar.}: SSLContext + + proc getSSLContext(): SSLContext = + if defaultSSLContext == nil: + defaultSSLContext = newContext(verifyMode = CVerifyNone) + result = defaultSSLContext proc createMessage*(mSubject, mBody: string, mTo, mCc: seq[string], otherHeaders: openarray[tuple[name, value: string]]): Message = @@ -109,20 +114,22 @@ proc `$`*(msg: Message): string = result.add(msg.msgBody) proc newSmtp*(useSsl = false, debug=false, - sslContext = defaultSslContext): Smtp = + sslContext: SSLContext = nil): Smtp = ## Creates a new ``Smtp`` instance. new result result.debug = debug - result.sock = newSocket() if useSsl: when compiledWithSsl: - sslContext.wrapSocket(result.sock) + if sslContext == nil: + getSSLContext().wrapSocket(result.sock) + else: + sslContext.wrapSocket(result.sock) else: {.error: "SMTP module compiled without SSL support".} proc newAsyncSmtp*(useSsl = false, debug=false, - sslContext = defaultSslContext): AsyncSmtp = + sslContext: SSLContext = nil): AsyncSmtp = ## Creates a new ``AsyncSmtp`` instance. new result result.debug = debug @@ -130,7 +137,10 @@ proc newAsyncSmtp*(useSsl = false, debug=false, result.sock = newAsyncSocket() if useSsl: when compiledWithSsl: - sslContext.wrapSocket(result.sock) + if sslContext == nil: + getSSLContext().wrapSocket(result.sock) + else: + sslContext.wrapSocket(result.sock) else: {.error: "SMTP module compiled without SSL support".} diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index e266275cf..4d0fe800e 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -149,8 +149,8 @@ proc isAlphaAscii*(s: string): bool {.noSideEffect, procvar, ## in `s`. runnableExamples: doAssert isAlphaAscii("fooBar") == true - doAssert isAlphaAscii("fooBar1") == false - doAssert isAlphaAscii("foo Bar") == false + doAssert isAlphaAscii("fooBar1") == false + doAssert isAlphaAscii("foo Bar") == false isImpl isAlphaAscii proc isAlphaNumeric*(s: string): bool {.noSideEffect, procvar, @@ -164,7 +164,7 @@ proc isAlphaNumeric*(s: string): bool {.noSideEffect, procvar, ## in `s`. runnableExamples: doAssert isAlphaNumeric("fooBar") == true - doAssert isAlphaNumeric("fooBar") == true + doAssert isAlphaNumeric("fooBar") == true doAssert isAlphaNumeric("foo Bar") == false isImpl isAlphaNumeric @@ -179,7 +179,7 @@ proc isDigit*(s: string): bool {.noSideEffect, procvar, ## in `s`. runnableExamples: doAssert isDigit("1908") == true - doAssert isDigit("fooBar1") == false + doAssert isDigit("fooBar1") == false isImpl isDigit proc isSpaceAscii*(s: string): bool {.noSideEffect, procvar, @@ -191,7 +191,7 @@ proc isSpaceAscii*(s: string): bool {.noSideEffect, procvar, ## characters and there is at least one character in `s`. runnableExamples: doAssert isSpaceAscii(" ") == true - doAssert isSpaceAscii("") == false + doAssert isSpaceAscii("") == false isImpl isSpaceAscii template isCaseImpl(s, charProc, skipNonAlpha) = @@ -420,7 +420,7 @@ proc toOctal*(c: char): string {.noSideEffect, rtl, extern: "nsuToOctal".} = ## The resulting string may not have a leading zero. Its length is always ## exactly 3. runnableExamples: - doAssert toOctal('!') == "041" + doAssert toOctal('!') == "041" result = newString(3) var val = ord(c) for i in countdown(2, 0): @@ -933,7 +933,7 @@ proc intToStr*(x: int, minchars: Positive = 1): string {.noSideEffect, ## achieved by adding leading zeros. runnableExamples: doAssert intToStr(1984) == "1984" - doAssert intToStr(1984, 6) == "001984" + doAssert intToStr(1984, 6) == "001984" result = $abs(x) for i in 1 .. minchars - len(result): result = '0' & result @@ -1211,7 +1211,8 @@ proc wordWrap*(s: string, maxLineWidth = 80, splitLongWords = true, seps: set[char] = Whitespace, newLine = "\n"): string {. - noSideEffect, rtl, extern: "nsuWordWrap".} = + noSideEffect, rtl, extern: "nsuWordWrap", + deprecated: "use wrapWords in std/wordwrap instead".} = ## Word wraps `s`. result = newStringOfCap(s.len + s.len shr 6) var spaceLeft = maxLineWidth @@ -1262,6 +1263,7 @@ proc indent*(s: string, count: Natural, padding: string = " "): string proc unindent*(s: string, count: Natural, padding: string = " "): string {.noSideEffect, rtl, extern: "nsuUnindent".} = ## Unindents each line in ``s`` by ``count`` amount of ``padding``. + ## Sometimes called `dedent`:idx: ## ## **Note:** This does not preserve the new line characters used in ``s``. runnableExamples: @@ -1525,6 +1527,8 @@ proc rfind*(s, sub: string, start: int = -1): int {.noSideEffect.} = ## backwards to 0. ## ## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned. + if sub.len == 0: + return -1 let realStart = if start == -1: s.len else: start for i in countdown(realStart-sub.len, 0): for j in 0..sub.len-1: @@ -1632,11 +1636,7 @@ proc replace*(s, sub: string, by = ""): string {.noSideEffect, result = "" let subLen = sub.len if subLen == 0: - for c in s: - add result, by - add result, c - add result, by - return + result = s elif subLen == 1: # when the pattern is a single char, we use a faster # char-based search that doesn't need a skip table: @@ -1691,21 +1691,22 @@ proc replaceWord*(s, sub: string, by = ""): string {.noSideEffect, initSkipTable(a, sub) var i = 0 let last = s.high - let sublen = max(sub.len, 1) - while true: - var j = find(a, s, sub, i, last) - if j < 0: break - # word boundary? - if (j == 0 or s[j-1] notin wordChars) and - (j+sub.len >= s.len or s[j+sub.len] notin wordChars): - add result, substr(s, i, j - 1) - add result, by - i = j + sublen - else: - add result, substr(s, i, j) - i = j + 1 - # copy the rest: - add result, substr(s, i) + let sublen = sub.len + if sublen > 0: + while true: + var j = find(a, s, sub, i, last) + if j < 0: break + # word boundary? + if (j == 0 or s[j-1] notin wordChars) and + (j+sub.len >= s.len or s[j+sub.len] notin wordChars): + add result, substr(s, i, j - 1) + add result, by + i = j + sublen + else: + add result, substr(s, i, j) + i = j + 1 + # 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 @@ -1722,15 +1723,18 @@ proc multiReplace*(s: string, replacements: varargs[(string, string)]): string { 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 + for sub, by in replacements.items: + if sub.len > 0: + # Include first character of all replacements + fastChk.incl sub[0] 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) + for sub, by in replacements.items: + if sub.len > 0 and s.continuesWith(sub, i): + add result, by + inc(i, sub.len) break sIteration # No matching replacement found # copy current character from s @@ -1983,8 +1987,6 @@ type ffDecimal, ## use decimal floating point notation ffScientific ## use scientific notation (using ``e`` character) -{.deprecated: [TFloatFormat: FloatFormatMode].} - proc formatBiggestFloat*(f: BiggestFloat, format: FloatFormatMode = ffDefault, precision: range[-1..32] = 16; decimalSep = '.'): string {. @@ -2510,6 +2512,7 @@ proc stripLineEnd*(s: var string) = ## Returns ``s`` stripped from one of these suffixes: ## ``\r, \n, \r\n, \f, \v`` (at most once instance). ## For example, can be useful in conjunction with ``osproc.execCmdEx``. + ## aka: `chomp`:idx: runnableExamples: var s = "foo\n\n" s.stripLineEnd @@ -2612,7 +2615,7 @@ when isMainModule: doAssert "-lda-ldz -ld abc".replaceWord("-ld") == "-lda-ldz abc" doAssert "-lda-ldz -ld abc".replaceWord("") == "-lda-ldz -ld abc" - doAssert "oo".replace("", "abc") == "abcoabcoabc" + doAssert "oo".replace("", "abc") == "oo" type MyEnum = enum enA, enB, enC, enuD, enE doAssert parseEnum[MyEnum]("enu_D") == enuD diff --git a/lib/pure/subexes.nim b/lib/pure/subexes.nim index 8149c72cc..d103af710 100644 --- a/lib/pure/subexes.nim +++ b/lib/pure/subexes.nim @@ -300,8 +300,6 @@ proc scanDollar(p: var FormatParser, a: openarray[string], s: var string) = type Subex* = distinct string ## string that contains a substitution expression -{.deprecated: [TSubex: Subex].} - proc subex*(s: string): Subex = ## constructs a *substitution expression* from `s`. Currently this performs ## no syntax checking but this may change in later versions. diff --git a/lib/pure/terminal.nim b/lib/pure/terminal.nim index 974dc839d..35dc2483c 100644 --- a/lib/pure/terminal.nim +++ b/lib/pure/terminal.nim @@ -481,9 +481,6 @@ type styleHidden, ## hidden text styleStrikethrough ## strikethrough -{.deprecated: [TStyle: Style].} -{.deprecated: [styleUnknown: styleItalic].} - when not defined(windows): var gFG {.threadvar.}: int @@ -556,9 +553,6 @@ type bg8Bit, ## 256-color (not supported, see ``enableTrueColors`` instead.) bgDefault ## default terminal background color -{.deprecated: [TForegroundColor: ForegroundColor, - TBackgroundColor: BackgroundColor].} - when defined(windows): var defaultForegroundColor, defaultBackgroundColor: int16 = 0xFFFF'i16 # Default to an invalid value 0xFFFF diff --git a/lib/pure/times.nim b/lib/pure/times.nim index 62284c6cb..fd1a6acc5 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -298,9 +298,6 @@ type TimeIntervalParts* = array[TimeUnit, int] # Array of Duration parts starts TimesMutableTypes = DateTime | Time | Duration | TimeInterval -{.deprecated: [TMonth: Month, TWeekDay: WeekDay, TTime: Time, - TTimeInterval: TimeInterval, TTimeInfo: DateTime, TimeInfo: DateTime].} - const secondsInMin = 60 secondsInHour = 60*60 @@ -748,8 +745,7 @@ proc abs*(a: Duration): Duration = initDuration(seconds = abs(a.seconds), nanoseconds = -a.nanosecond) proc toTime*(dt: DateTime): Time {.tags: [], raises: [], benign.} = - ## Converts a broken-down time structure to - ## calendar time representation. + ## Converts a ``DateTime`` to a ``Time`` representing the same point in time. let epochDay = toEpochday(dt.monthday, dt.month, dt.year) var seconds = epochDay * secondsInDay seconds.inc dt.hour * secondsInHour @@ -843,6 +839,11 @@ proc `$`*(zone: Timezone): string = proc `==`*(zone1, zone2: Timezone): bool = ## Two ``Timezone``'s are considered equal if their name is equal. + if system.`==`(zone1, zone2): + return true + if zone1.isNil or zone2.isNil: + return false + runnableExamples: doAssert local() == local() doAssert local() != utc() @@ -1050,7 +1051,7 @@ proc local*(t: Time): DateTime = t.inZone(local()) proc getTime*(): Time {.tags: [TimeEffect], benign.} = - ## Gets the current time as a ``Time`` with nanosecond resolution. + ## Gets the current time as a ``Time`` with up to nanosecond resolution. when defined(JS): let millis = newDate().getTime() let seconds = convert(Milliseconds, Seconds, millis) @@ -1144,16 +1145,16 @@ proc `-`*(ti1, ti2: TimeInterval): TimeInterval = result = ti1 + (-ti2) proc getDateStr*(): string {.rtl, extern: "nt$1", tags: [TimeEffect].} = - ## Gets the current date as a string of the format ``YYYY-MM-DD``. - var ti = now() - result = $ti.year & '-' & intToStr(ord(ti.month), 2) & - '-' & intToStr(ti.monthday, 2) + ## Gets the current local date as a string of the format ``YYYY-MM-DD``. + var dt = now() + result = $dt.year & '-' & intToStr(ord(dt.month), 2) & + '-' & intToStr(dt.monthday, 2) proc getClockStr*(): string {.rtl, extern: "nt$1", tags: [TimeEffect].} = - ## Gets the current clock time as a string of the format ``HH:MM:SS``. - var ti = now() - result = intToStr(ti.hour, 2) & ':' & intToStr(ti.minute, 2) & - ':' & intToStr(ti.second, 2) + ## Gets the current local clock time as a string of the format ``HH:MM:SS``. + var dt = now() + result = intToStr(dt.hour, 2) & ':' & intToStr(dt.minute, 2) & + ':' & intToStr(dt.second, 2) proc toParts* (ti: TimeInterval): TimeIntervalParts = ## Converts a `TimeInterval` into an array consisting of its time units, @@ -1385,7 +1386,6 @@ proc `==`*(a, b: DateTime): bool = ## Returns true if ``a == b``, that is if both dates represent the same point in time. return a.toTime == b.toTime - proc isStaticInterval(interval: TimeInterval): bool = interval.years == 0 and interval.months == 0 and interval.days == 0 and interval.weeks == 0 @@ -1400,28 +1400,20 @@ proc evaluateStaticInterval(interval: TimeInterval): Duration = hours = interval.hours) proc between*(startDt, endDt: DateTime): TimeInterval = - ## Evaluate difference between two dates in ``TimeInterval`` format, so, it - ## will be relative. + ## Gives the difference between ``startDt`` and ``endDt`` as a + ## ``TimeInterval``. ## - ## **Warning:** It's not recommended to use ``between`` for ``DateTime's`` in - ## different ``TimeZone's``. - ## ``a + between(a, b) == b`` is only guaranteed when ``a`` and ``b`` are in UTC. + ## **Warning:** This proc currently gives very few guarantees about the + ## result. ``a + between(a, b) == b`` is **not** true in general + ## (it's always true when UTC is used however). Neither is it guaranteed that + ## all components in the result will have the same sign. The behavior of this + ## proc might change in the future. runnableExamples: - var a = initDateTime(year = 2018, month = Month(3), monthday = 25, - hour = 0, minute = 59, second = 59, nanosecond = 1, - zone = utc()).local - var b = initDateTime(year = 2018, month = Month(3), monthday = 25, - hour = 1, minute = 1, second = 1, nanosecond = 0, - zone = utc()).local - doAssert between(a, b) == initTimeInterval( - nanoseconds=999, milliseconds=999, microseconds=999, seconds=1, minutes=1) - - a = parse("2018-01-09T00:00:00+00:00", "yyyy-MM-dd'T'HH:mm:sszzz", utc()) - b = parse("2018-01-10T23:00:00-02:00", "yyyy-MM-dd'T'HH:mm:sszzz") - doAssert between(a, b) == initTimeInterval(hours=1, days=2) - ## Though, here correct answer should be 1 day 25 hours (cause this day in - ## this tz is actually 26 hours). That's why operating different TZ is - ## discouraged + var a = initDateTime(25, mMar, 2015, 12, 0, 0, utc()) + var b = initDateTime(1, mApr, 2017, 15, 0, 15, utc()) + var ti = initTimeInterval(years = 2, days = 7, hours = 3, seconds = 15) + doAssert between(a, b) == ti + doAssert between(a, b) == -between(b, a) var startDt = startDt.utc() var endDt = endDt.utc() @@ -1549,7 +1541,6 @@ proc `*=`*[T: TimesMutableTypes, U](a: var T, b: U) = var dur = initDuration(seconds = 1) dur *= 5 doAssert dur == initDuration(seconds = 5) - a = a * b # @@ -1813,7 +1804,7 @@ proc formatPattern(dt: DateTime, pattern: FormatPattern, result: var string) = of UUUU: result.add $dt.year of z, zz, zzz, zzzz: - if dt.timezone.name == "Etc/UTC": + if dt.timezone != nil and dt.timezone.name == "Etc/UTC": result.add 'Z' else: result.add if -dt.utcOffset >= 0: '+' else: '-' @@ -2026,9 +2017,9 @@ proc parsePattern(input: string, pattern: FormatPattern, i: var int, var offset = 0 case pattern of z: - offset = takeInt(1..2) * -3600 + offset = takeInt(1..2) * 3600 of zz: - offset = takeInt(2..2) * -3600 + offset = takeInt(2..2) * 3600 of zzz: offset.inc takeInt(2..2) * 3600 if input[i] != ':': @@ -2508,4 +2499,4 @@ proc zoneInfoFromUtc*(zone: Timezone, time: Time): ZonedTime proc zoneInfoFromTz*(zone: Timezone, adjTime: Time): ZonedTime {.deprecated: "Use zonedTimeFromAdjTime instead".} = ## **Deprecated since v0.19.0:** use the ``zonedTimeFromAdjTime`` instead. - zone.zonedTimeFromAdjTime(adjTime) \ No newline at end of file + zone.zonedTimeFromAdjTime(adjTime) diff --git a/lib/pure/unicode.nim b/lib/pure/unicode.nim index 0107e2715..664765954 100644 --- a/lib/pure/unicode.nim +++ b/lib/pure/unicode.nim @@ -18,8 +18,6 @@ type Rune* = distinct RuneImpl ## type that can hold any Unicode character Rune16* = distinct int16 ## 16 bit Unicode character -{.deprecated: [TRune: Rune, TRune16: Rune16].} - proc `<=%`*(a, b: Rune): bool = return int(a) <=% int(b) proc `<%`*(a, b: Rune): bool = return int(a) <% int(b) proc `==`*(a, b: Rune): bool = return int(a) == int(b) @@ -213,6 +211,10 @@ proc toUTF8*(c: Rune): string {.rtl, extern: "nuc$1".} = result = "" fastToUTF8Copy(c, result, 0, false) +proc add*(s: var string; c: Rune) = + let pos = s.len + fastToUTF8Copy(c, s, pos, false) + proc `$`*(rune: Rune): string = ## Converts a Rune to a string rune.toUTF8 @@ -220,7 +222,8 @@ proc `$`*(rune: Rune): string = proc `$`*(runes: seq[Rune]): string = ## Converts a sequence of Runes to a string result = "" - for rune in runes: result.add(rune.toUTF8) + for rune in runes: + result.add rune proc runeOffset*(s: string, pos:Natural, start: Natural = 0): int = ## Returns the byte position of unicode character @@ -228,7 +231,7 @@ proc runeOffset*(s: string, pos:Natural, start: Natural = 0): int = ## returns the special value -1 if it runs out of the string ## ## Beware: This can lead to unoptimized code and slow execution! - ## Most problems are solve more efficient by using an iterator + ## Most problems can be solved more efficiently by using an iterator ## or conversion to a seq of Rune. var i = 0 @@ -244,7 +247,7 @@ proc runeAtPos*(s: string, pos: int): Rune = ## Returns the unicode character at position pos ## ## Beware: This can lead to unoptimized code and slow execution! - ## Most problems are solve more efficient by using an iterator + ## Most problems can be solved more efficiently by using an iterator ## or conversion to a seq of Rune. fastRuneAt(s, runeOffset(s, pos), result, false) @@ -252,7 +255,7 @@ proc runeStrAtPos*(s: string, pos: Natural): string = ## Returns the unicode character at position pos as UTF8 String ## ## Beware: This can lead to unoptimized code and slow execution! - ## Most problems are solve more efficient by using an iterator + ## Most problems can be solved more efficiently by using an iterator ## or conversion to a seq of Rune. let o = runeOffset(s, pos) s[o.. (o+runeLenAt(s, o)-1)] @@ -266,7 +269,7 @@ proc runeReverseOffset*(s: string, rev:Positive): (int, int) = ## satisfy the request. ## ## Beware: This can lead to unoptimized code and slow execution! - ## Most problems are solve more efficient by using an iterator + ## Most problems can be solved more efficiently by using an iterator ## or conversion to a seq of Rune. var a = rev.int @@ -1963,12 +1966,12 @@ proc align*(s: string, count: Natural, padding = ' '.Rune): string {. ## returned unchanged. If you need to left align a string use the `alignLeft ## proc <#alignLeft>`_. runnableExamples: - assert align("abc", 4) == " abc" - assert align("a", 0) == "a" - assert align("1232", 6) == " 1232" - assert align("1232", 6, '#'.Rune) == "##1232" - assert align("Åge", 5) == " Åge" - assert align("×", 4, '_'.Rune) == "___×" + assert align("abc", 4) == " abc" + assert align("a", 0) == "a" + assert align("1232", 6) == " 1232" + assert align("1232", 6, '#'.Rune) == "##1232" + assert align("Åge", 5) == " Åge" + assert align("×", 4, '_'.Rune) == "___×" let sLen = s.runeLen if sLen < count: diff --git a/lib/pure/unittest.nim b/lib/pure/unittest.nim index 757bf4745..837072be2 100644 --- a/lib/pure/unittest.nim +++ b/lib/pure/unittest.nim @@ -145,8 +145,6 @@ type testStartTime: float testStackTrace: string -{.deprecated: [TTestStatus: TestStatus, TOutputLevel: OutputLevel]} - var abortOnError* {.threadvar.}: bool ## Set to true in order to quit ## immediately on fail. Default is false, diff --git a/lib/pure/editdistance.nim b/lib/std/editdistance.nim index 40beb7d93..40beb7d93 100644 --- a/lib/pure/editdistance.nim +++ b/lib/std/editdistance.nim diff --git a/lib/std/wordwrap.nim b/lib/std/wordwrap.nim new file mode 100644 index 000000000..c7898b339 --- /dev/null +++ b/lib/std/wordwrap.nim @@ -0,0 +1,90 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2018 Nim contributors +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## This module contains an algorithm to wordwrap a Unicode string. + +import strutils, unicode + +proc olen(s: string): int = + var i = 0 + result = 0 + while i < s.len: + inc result + let L = graphemeLen(s, i) + inc i, L + +proc wrapWords*(s: string, maxLineWidth = 80, + splitLongWords = true, + seps: set[char] = Whitespace, + newLine = "\n"): string {.noSideEffect.} = + ## Word wraps `s`. + result = newStringOfCap(s.len + s.len shr 6) + var spaceLeft = maxLineWidth + var lastSep = "" + for word, isSep in tokenize(s, seps): + let wlen = olen(word) + if isSep: + lastSep = word + spaceLeft = spaceLeft - wlen + elif wlen > spaceLeft: + if splitLongWords and wlen > maxLineWidth: + var i = 0 + while i < word.len: + if spaceLeft <= 0: + spaceLeft = maxLineWidth + result.add newLine + dec spaceLeft + let L = graphemeLen(word, i) + for j in 0 ..< L: result.add word[i+j] + inc i, L + else: + spaceLeft = maxLineWidth - wlen + result.add(newLine) + result.add(word) + else: + spaceLeft = spaceLeft - wlen + result.add(lastSep) + result.add(word) + lastSep.setLen(0) + +when isMainModule: + + when true: + let + inp = """ this is a long text -- muchlongerthan10chars and here + it goes""" + outp = " this is a\nlong text\n--\nmuchlongerthan10chars\nand here\nit goes" + doAssert wrapWords(inp, 10, false) == outp + + let + longInp = """ThisIsOneVeryLongStringWhichWeWillSplitIntoEightSeparatePartsNow""" + longOutp = "ThisIsOn\neVeryLon\ngStringW\nhichWeWi\nllSplitI\nntoEight\nSeparate\nPartsNow" + doAssert wrapWords(longInp, 8, true) == longOutp + + # test we don't break Umlauts into invalid bytes: + let fies = "äöüöäöüöäöüöäöüööäöüöäößßßßüöäößßßßßß" + let fiesRes = "ä\nö\nü\nö\nä\nö\nü\nö\nä\nö\nü\nö\nä\nö\nü\nö\nö\nä\nö\nü\nö\nä\nö\nß\nß\nß\nß\nü\nö\nä\nö\nß\nß\nß\nß\nß\nß" + doAssert wrapWords(fies, 1, true) == fiesRes + + let longlongword = """abc uitdaeröägfßhydüäpydqfü,träpydqgpmüdträpydföägpydörztdüöäfguiaeowäzjdtrüöäp psnrtuiydrözenrüöäpyfdqazpesnrtulocjtüö +äzydgyqgfqfgprtnwjlcydkqgfüöezmäzydydqüüöäpdtrnvwfhgckdumböäpydfgtdgfhtdrntdrntydfogiayqfguiatrnydrntüöärtniaoeydfgaoeiqfglwcßqfgxvlcwgtfhiaoen +rsüöäapmböäptdrniaoydfglckqfhouenrtsüöäptrniaoeyqfgulocfqclgwxßqflgcwßqfxglcwrniatrnmüböäpmöäbpümöäbpüöämpbaoestnriaesnrtdiaesrtdniaesdrtnaetdr +iaoenvlcyfglwckßqfgvwkßqgfvlwkßqfgvlwckßqvlwkgfUIαοιαοιαχολωχσωχνωκψρχκψρτιεαοσηζϵηζιοεννκεωνιαλωσωκνκψρκγτφγτχκγτεκργτιχνκιωχσιλωσλωχξλξλξωχωχ +ξχλωωχαοεοιαεοαεοιαεοαεοιαοεσναοεκνρκψγκψφϵιηαααοε""" + let longlongwordRes = """ +abc uitdaeröägfßhydüäpydqfü,träpydqgpmüdträpydföägpydörztdüöäfguiaeowäzjdtrüöäp +psnrtuiydrözenrüöäpyfdqazpesnrtulocjtüöäzydgyqgfqfgprtnwjlcydkqgfüöezmäzydydqüü +öäpdtrnvwfhgckdumböäpydfgtdgfhtdrntdrntydfogiayqfguiatrnydrntüöärtniaoeydfgaoeiq +fglwcßqfgxvlcwgtfhiaoenrsüöäapmböäptdrniaoydfglckqfhouenrtsüöäptrniaoeyqfgulocf +qclgwxßqflgcwßqfxglcwrniatrnmüböäpmöäbpümöäbpüöämpbaoestnriaesnrtdiaesrtdniaesdr +tnaetdriaoenvlcyfglwckßqfgvwkßqgfvlwkßqfgvlwckßqvlwkgfUIαοιαοιαχολωχσωχνωκψρχκψ +ρτιεαοσηζϵηζιοεννκεωνιαλωσωκνκψρκγτφγτχκγτεκργτιχνκιωχσιλωσλωχξλξλξωχωχ +ξχλωωχαοεοιαεοαεοιαεοαεοιαοεσναοεκνρκψγκψφϵιηαααοε""" + doAssert wrapWords(longlongword) == longlongwordRes + diff --git a/lib/system.nim b/lib/system.nim index db1d3ca1e..e8b427c55 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -106,8 +106,6 @@ type SomeNumber* = SomeInteger|SomeFloat ## type class matching all number types -{.deprecated: [SomeReal: SomeFloat].} - proc defined*(x: untyped): bool {.magic: "Defined", noSideEffect, compileTime.} ## Special compile-time procedure that checks whether `x` is ## defined. @@ -2041,11 +2039,11 @@ proc getRefcount*[T](x: seq[T]): int {.importc: "getRefcount", noSideEffect, const - Inf* {.magic: "Inf".} = 1.0 / 0.0 + Inf* = 0x7FF0000000000000'f64 ## contains the IEEE floating point value of positive infinity. - NegInf* {.magic: "NegInf".} = -Inf + NegInf* = 0xFFF0000000000000'f64 ## contains the IEEE floating point value of negative infinity. - NaN* {.magic: "NaN".} = 0.0 / 0.0 + NaN* = 0x7FF7FFFFFFFFFFFF'f64 ## contains an IEEE floating point value of *Not A Number*. Note ## that you cannot compare a floating point value to this value ## and expect a reasonable result - use the `classify` procedure @@ -2056,7 +2054,7 @@ const NimMinor* {.intdefine.}: int = 19 ## is the minor number of Nim's version. - NimPatch* {.intdefine.}: int = 1 + NimPatch* {.intdefine.}: int = 9 ## is the patch number of Nim's version. NimVersion*: string = $NimMajor & "." & $NimMinor & "." & $NimPatch @@ -2253,10 +2251,10 @@ proc min*(x, y: float): float {.magic: "MinF64", noSideEffect.} = proc max*(x, y: float): float {.magic: "MaxF64", noSideEffect.} = if y <= x: x else: y -proc min*[T](x, y: T): T = +proc min*[T](x, y: T): T {.inline.}= if x <= y: x else: y -proc max*[T](x, y: T): T = +proc max*[T](x, y: T): T {.inline.}= if y <= x: x else: y {.pop.} @@ -4019,6 +4017,7 @@ proc shallow*[T](s: var seq[T]) {.noSideEffect, inline.} = ## marks a sequence `s` as `shallow`:idx:. Subsequent assignments will not ## perform deep copies of `s`. This is only useful for optimization ## purposes. + if s.len == 0: return when not defined(JS) and not defined(nimscript): var s = cast[PGenericSeq](s) s.reserved = s.reserved or seqShallowFlag @@ -4029,6 +4028,8 @@ proc shallow*(s: var string) {.noSideEffect, inline.} = ## purposes. when not defined(JS) and not defined(nimscript) and not defined(gcDestructors): var s = cast[PGenericSeq](s) + if s == nil: + s = cast[PGenericSeq](newString(0)) # string literals cannot become 'shallow': if (s.reserved and strlitFlag) == 0: s.reserved = s.reserved or seqShallowFlag @@ -4039,8 +4040,6 @@ type NimNode* {.magic: "PNimrodNode".} = ref NimNodeObj ## represents a Nim AST node. Macros operate on this type. -{.deprecated: [PNimrodNode: NimNode].} - when false: template eval*(blk: typed): typed = ## executes a block of code at compile time just as if it was a macro diff --git a/lib/system/ansi_c.nim b/lib/system/ansi_c.nim index 67e42c0af..38910dbd6 100644 --- a/lib/system/ansi_c.nim +++ b/lib/system/ansi_c.nim @@ -50,7 +50,8 @@ when defined(windows): SIGTERM = cint(15) elif defined(macosx) or defined(linux) or defined(freebsd) or defined(openbsd) or defined(netbsd) or defined(solaris) or - defined(dragonfly) or defined(nintendoswitch) or defined(genode): + defined(dragonfly) or defined(nintendoswitch) or defined(genode) or + defined(aix): const SIGABRT = cint(6) SIGFPE = cint(8) diff --git a/lib/system/atomics.nim b/lib/system/atomics.nim index 56ebde823..b41b4009a 100644 --- a/lib/system/atomics.nim +++ b/lib/system/atomics.nim @@ -40,7 +40,6 @@ when someGcc and hasThreadSupport: type AtomType* = SomeNumber|pointer|ptr|char|bool ## Type Class representing valid types for use with atomic procs - {.deprecated: [TAtomType: AtomType].} proc atomicLoadN*[T: AtomType](p: ptr T, mem: AtomMemModel): T {. importc: "__atomic_load_n", nodecl.} diff --git a/lib/system/cellsets.nim b/lib/system/cellsets.nim index f26cb86ab..d56de06bb 100644 --- a/lib/system/cellsets.nim +++ b/lib/system/cellsets.nim @@ -39,8 +39,7 @@ type CellSeq {.final, pure.} = object len, cap: int d: PCellArray -{.deprecated: [TCell: Cell, TBitIndex: BitIndex, TPageDesc: PageDesc, - TRefCount: RefCount, TCellSet: CellSet, TCellSeq: CellSeq].} + # ------------------- cell seq handling --------------------------------------- proc contains(s: CellSeq, c: PCell): bool {.inline.} = diff --git a/lib/system/debugger.nim b/lib/system/debugger.nim index 937c0d6f0..fd0ae2399 100644 --- a/lib/system/debugger.nim +++ b/lib/system/debugger.nim @@ -23,7 +23,6 @@ type # except for the global data description. f: TFrame slots: array[0..10_000, VarSlot] -{.deprecated: [TVarSlot: VarSlot, TExtendedFrame: ExtendedFrame].} var dbgGlobalData: ExtendedFrame # this reserves much space, but @@ -156,7 +155,6 @@ type address: pointer typ: PNimType oldValue: Hash -{.deprecated: [THash: Hash, TWatchpoint: Watchpoint].} var watchpoints: array[0..99, Watchpoint] diff --git a/lib/system/dyncalls.nim b/lib/system/dyncalls.nim index d6c361b2c..70bdc429b 100644 --- a/lib/system/dyncalls.nim +++ b/lib/system/dyncalls.nim @@ -17,13 +17,6 @@ const NilLibHandle: LibHandle = nil -proc c_fwrite(buf: pointer, size, n: csize, f: File): cint {. - importc: "fwrite", header: "<stdio.h>".} - -proc rawWrite(f: File, s: string|cstring) = - # we cannot throw an exception here! - discard c_fwrite(cstring(s), 1, s.len, f) - proc nimLoadLibraryError(path: string) = # carefully written to avoid memory allocation: stderr.rawWrite("could not load: ") diff --git a/lib/system/endb.nim b/lib/system/endb.nim index d51ae29df..257ee3fea 100644 --- a/lib/system/endb.nim +++ b/lib/system/endb.nim @@ -35,8 +35,6 @@ type dbSkipCurrent, dbQuiting, # debugger wants to quit dbBreakpoints # debugger is only interested in breakpoints -{.deprecated: [TStaticStr: StaticStr, TBreakpointFilename: BreakpointFilename, - TDbgState: DbgState].} var dbgUser: StaticStr # buffer for user input; first command is ``step_into`` diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim index 778137668..a6da8f5a3 100644 --- a/lib/system/excpt.nim +++ b/lib/system/excpt.nim @@ -17,8 +17,15 @@ var ## instead of stdmsg.write when printing stacktrace. ## Unstable API. +proc c_fwrite(buf: pointer, size, n: csize, f: File): cint {. + importc: "fwrite", header: "<stdio.h>".} + +proc rawWrite(f: File, s: string|cstring) = + # we cannot throw an exception here! + discard c_fwrite(cstring(s), 1, s.len, f) + when not defined(windows) or not defined(guiapp): - proc writeToStdErr(msg: cstring) = write(stdmsg, msg) + proc writeToStdErr(msg: cstring) = rawWrite(stdmsg, msg) else: proc MessageBoxA(hWnd: cint, lpText, lpCaption: cstring, uType: int): int32 {. diff --git a/lib/system/gc_common.nim b/lib/system/gc_common.nim index c9866164e..565453298 100644 --- a/lib/system/gc_common.nim +++ b/lib/system/gc_common.nim @@ -157,46 +157,6 @@ else: iterator items(first: var GcStack): ptr GcStack = yield addr(first) proc len(stack: var GcStack): int = 1 -proc stackSize(stack: ptr GcStack): int {.noinline.} = - when nimCoroutines: - var pos = stack.pos - else: - var pos {.volatile.}: pointer - pos = addr(pos) - - if pos != nil: - when defined(stackIncreases): - result = cast[ByteAddress](pos) -% cast[ByteAddress](stack.bottom) - else: - result = cast[ByteAddress](stack.bottom) -% cast[ByteAddress](pos) - else: - result = 0 - -proc stackSize(): int {.noinline.} = - for stack in gch.stack.items(): - result = result + stack.stackSize() - -when nimCoroutines: - proc setPosition(stack: ptr GcStack, position: pointer) = - stack.pos = position - stack.maxStackSize = max(stack.maxStackSize, stack.stackSize()) - - proc setPosition(stack: var GcStack, position: pointer) = - setPosition(addr(stack), position) - - proc getActiveStack(gch: var GcHeap): ptr GcStack = - return gch.activeStack - - proc isActiveStack(stack: ptr GcStack): bool = - return gch.activeStack == stack -else: - # Stack positions do not need to be tracked if coroutines are not used. - proc setPosition(stack: ptr GcStack, position: pointer) = discard - proc setPosition(stack: var GcStack, position: pointer) = discard - # There is just one stack - main stack of the thread. It is active always. - proc getActiveStack(gch: var GcHeap): ptr GcStack = addr(gch.stack) - proc isActiveStack(stack: ptr GcStack): bool = true - when declared(threadType): proc setupForeignThreadGc*() {.gcsafe.} = ## Call this if you registered a callback that will be run from a thread not @@ -246,6 +206,46 @@ elif defined(hppa) or defined(hp9000) or defined(hp9000s300) or else: const stackIncreases = false +proc stackSize(stack: ptr GcStack): int {.noinline.} = + when nimCoroutines: + var pos = stack.pos + else: + var pos {.volatile.}: pointer + pos = addr(pos) + + if pos != nil: + when stackIncreases: + result = cast[ByteAddress](pos) -% cast[ByteAddress](stack.bottom) + else: + result = cast[ByteAddress](stack.bottom) -% cast[ByteAddress](pos) + else: + result = 0 + +proc stackSize(): int {.noinline.} = + for stack in gch.stack.items(): + result = result + stack.stackSize() + +when nimCoroutines: + proc setPosition(stack: ptr GcStack, position: pointer) = + stack.pos = position + stack.maxStackSize = max(stack.maxStackSize, stack.stackSize()) + + proc setPosition(stack: var GcStack, position: pointer) = + setPosition(addr(stack), position) + + proc getActiveStack(gch: var GcHeap): ptr GcStack = + return gch.activeStack + + proc isActiveStack(stack: ptr GcStack): bool = + return gch.activeStack == stack +else: + # Stack positions do not need to be tracked if coroutines are not used. + proc setPosition(stack: ptr GcStack, position: pointer) = discard + proc setPosition(stack: var GcStack, position: pointer) = discard + # There is just one stack - main stack of the thread. It is active always. + proc getActiveStack(gch: var GcHeap): ptr GcStack = addr(gch.stack) + proc isActiveStack(stack: ptr GcStack): bool = true + {.push stack_trace: off.} when nimCoroutines: proc GC_addStack(bottom: pointer) {.cdecl, exportc.} = diff --git a/lib/system/mmdisp.nim b/lib/system/mmdisp.nim index 9a7af0a28..683e84f7d 100644 --- a/lib/system/mmdisp.nim +++ b/lib/system/mmdisp.nim @@ -37,7 +37,6 @@ type ByteArray = UncheckedArray[byte] PByte = ptr ByteArray PString = ptr string -{.deprecated: [TByteArray: ByteArray].} # Page size of the system; in most cases 4096 bytes. For exotic OS or # CPU this needs to be changed: diff --git a/lib/system/nimscript.nim b/lib/system/nimscript.nim index 0adc7a83c..fc4b574e4 100644 --- a/lib/system/nimscript.nim +++ b/lib/system/nimscript.nim @@ -32,7 +32,7 @@ proc listFiles*(dir: string): seq[string] = ## Lists all the files (non-recursively) in the directory `dir`. builtin -proc removeDir(dir: string){. +proc removeDir(dir: string) {. tags: [ReadIOEffect, WriteIOEffect], raises: [OSError].} = builtin proc removeFile(dir: string) {. tags: [ReadIOEffect, WriteIOEffect], raises: [OSError].} = builtin diff --git a/lib/system/profiler.nim b/lib/system/profiler.nim index 7146500d9..ffd6fd0c5 100644 --- a/lib/system/profiler.nim +++ b/lib/system/profiler.nim @@ -23,7 +23,6 @@ type lines*: array[0..MaxTraceLen-1, cstring] files*: array[0..MaxTraceLen-1, cstring] ProfilerHook* = proc (st: StackTrace) {.nimcall.} -{.deprecated: [TStackTrace: StackTrace, TProfilerHook: ProfilerHook].} proc `[]`*(st: StackTrace, i: int): cstring = st.lines[i] diff --git a/lib/system/repr.nim b/lib/system/repr.nim index ce7c349bd..ff8f92404 100644 --- a/lib/system/repr.nim +++ b/lib/system/repr.nim @@ -127,7 +127,6 @@ type marked: CellSet recdepth: int # do not recurse endlessly indent: int # indentation -{.deprecated: [TReprClosure: ReprClosure].} when not defined(useNimRtl): proc initReprClosure(cl: var ReprClosure) = diff --git a/lib/system/sets.nim b/lib/system/sets.nim index 53d222468..a5f6c5de9 100644 --- a/lib/system/sets.nim +++ b/lib/system/sets.nim @@ -11,7 +11,6 @@ type NimSet = array[0..4*2048-1, uint8] -{.deprecated: [TNimSet: NimSet].} proc countBits32(n: int32): int {.compilerproc.} = var v = n diff --git a/lib/system/syslocks.nim b/lib/system/syslocks.nim index 6569f4f9f..fa4164b95 100644 --- a/lib/system/syslocks.nim +++ b/lib/system/syslocks.nim @@ -26,8 +26,6 @@ when defined(Windows): SysCond = Handle - {.deprecated: [THandle: Handle, TSysLock: SysLock, TSysCond: SysCond].} - proc initSysLock(L: var SysLock) {.importc: "InitializeCriticalSection", header: "<windows.h>".} ## Initializes the lock `L`. diff --git a/lib/system/threads.nim b/lib/system/threads.nim index aaf0164fd..f89de4376 100644 --- a/lib/system/threads.nim +++ b/lib/system/threads.nim @@ -57,7 +57,6 @@ when defined(windows): type SysThread* = Handle WinThreadProc = proc (x: pointer): int32 {.stdcall.} - {.deprecated: [TSysThread: SysThread].} proc createThread(lpThreadAttributes: pointer, dwStackSize: int32, lpStartAddress: WinThreadProc, @@ -198,8 +197,6 @@ else: Timespec {.importc: "struct timespec", header: "<time.h>".} = object tv_sec: Time tv_nsec: clong - {.deprecated: [TSysThread: SysThread, Tpthread_attr: PThreadAttr, - Ttimespec: Timespec, TThreadVarSlot: ThreadVarSlot].} proc pthread_attr_init(a1: var PthreadAttr) {. importc, header: pthreadh.} @@ -277,7 +274,6 @@ type stackSize: int else: nil -{.deprecated: [TThreadLocalStorage: ThreadLocalStorage, TGcThread: GcThread].} when not defined(useNimRtl): when not useStackMaskHack: @@ -382,8 +378,6 @@ type dataFn: proc (m: TArg) {.nimcall, gcsafe.} data: TArg -{.deprecated: [TThread: Thread].} - var threadDestructionHandlers {.rtlThreadVar.}: seq[proc () {.closure, gcsafe.}] diff --git a/lib/system/timers.nim b/lib/system/timers.nim index f2ebad2c1..dd8350e2d 100644 --- a/lib/system/timers.nim +++ b/lib/system/timers.nim @@ -13,7 +13,6 @@ type Ticks = distinct int64 Nanos = int64 -{.deprecated: [TTicks: Ticks, TNanos: Nanos].} when defined(windows): @@ -38,7 +37,6 @@ elif defined(macosx): importc: "mach_timebase_info_data_t", header: "<mach/mach_time.h>".} = object numer, denom: int32 - {.deprecated: [TMachTimebaseInfoData: MachTimebaseInfoData].} proc mach_absolute_time(): int64 {.importc, header: "<mach/mach.h>".} proc mach_timebase_info(info: var MachTimebaseInfoData) {.importc, @@ -61,7 +59,6 @@ elif defined(posixRealtime): final, pure.} = object ## struct timespec tv_sec: int ## Seconds. tv_nsec: int ## Nanoseconds. - {.deprecated: [TClockid: Clockid, TTimeSpec: TimeSpec].} var CLOCK_REALTIME {.importc: "CLOCK_REALTIME", header: "<time.h>".}: Clockid @@ -89,7 +86,7 @@ else: final, pure.} = object ## struct timeval tv_sec: Time ## Seconds. tv_usec: clong ## Microseconds. - {.deprecated: [Ttimeval: Timeval].} + proc posix_gettimeofday(tp: var Timeval, unused: pointer = nil) {. importc: "gettimeofday", header: "<sys/time.h>".} diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim index 73c222790..3e37b824c 100644 --- a/lib/windows/winlean.nim +++ b/lib/windows/winlean.nim @@ -22,10 +22,8 @@ const when useWinUnicode: type WinChar* = Utf16Char - {.deprecated: [TWinChar: WinChar].} else: type WinChar* = char - {.deprecated: [TWinChar: WinChar].} type Handle* = int @@ -96,10 +94,6 @@ type dwPlatformId*: DWORD szCSDVersion*: array[0..127, WinChar] -{.deprecated: [THandle: Handle, TSECURITY_ATTRIBUTES: SECURITY_ATTRIBUTES, - TSTARTUPINFO: STARTUPINFO, TPROCESS_INFORMATION: PROCESS_INFORMATION, - TFILETIME: FILETIME, TBY_HANDLE_FILE_INFORMATION: BY_HANDLE_FILE_INFORMATION].} - const STARTF_USESHOWWINDOW* = 1'i32 STARTF_USESTDHANDLES* = 256'i32 @@ -327,7 +321,6 @@ type dwReserved1: int32 cFileName*: array[0..(MAX_PATH) - 1, WinChar] cAlternateFileName*: array[0..13, WinChar] -{.deprecated: [TWIN32_FIND_DATA: WIN32_FIND_DATA].} when useWinUnicode: proc findFirstFileW*(lpFileName: WideCString, @@ -453,7 +446,6 @@ proc wsaGetLastError*(): cint {.importc: "WSAGetLastError", dynlib: ws2dll.} type SocketHandle* = distinct int -{.deprecated: [TSocketHandle: SocketHandle].} type WSAData* {.importc: "WSADATA", header: "winsock2.h".} = object @@ -538,11 +530,6 @@ type ai_next*: ptr AddrInfo ## Pointer to next in list. SockLen* = cuint -{.deprecated: [TSockaddr_in: Sockaddr_in, TAddrinfo: AddrInfo, - TSockAddr: SockAddr, TSockLen: SockLen, TTimeval: Timeval, - TWSADATA: WSADATA, Thostent: Hostent, TServent: Servent, - TInAddr: InAddr, Tin6_addr: In6_addr, Tsockaddr_in6: Sockaddr_in6, - Tsockaddr_in6_old: Sockaddr_in6_old].} var @@ -668,7 +655,6 @@ const type WOHandleArray* = array[0..MAXIMUM_WAIT_OBJECTS - 1, Handle] PWOHandleArray* = ptr WOHandleArray -{.deprecated: [TWOHandleArray: WOHandleArray].} proc waitForMultipleObjects*(nCount: DWORD, lpHandles: PWOHandleArray, bWaitAll: WINBOOL, dwMilliseconds: DWORD): DWORD{. @@ -798,7 +784,6 @@ type D2*: int16 D3*: int16 D4*: array[0..7, int8] -{.deprecated: [TOVERLAPPED: OVERLAPPED, TGUID: GUID].} const ERROR_IO_PENDING* = 997 # a.k.a WSA_IO_PENDING diff --git a/lib/wrappers/odbcsql.nim b/lib/wrappers/odbcsql.nim index 43d1c504d..3b022290b 100644 --- a/lib/wrappers/odbcsql.nim +++ b/lib/wrappers/odbcsql.nim @@ -60,17 +60,6 @@ type PSQLDOUBLE* = ptr TSqlDouble PSQLFLOAT* = ptr TSqlFloat PSQLHANDLE* = ptr SqlHandle -{.deprecated: [ - # TSqlChar: TSqlChar, # Name conflict if we drop`T` - # TSqlSmallInt: TSqlSmallInt, # Name conflict if we drop`T` - TSqlUSmallInt: SqlUSmallInt, TSqlHandle: SqlHandle, TSqlHEnv: SqlHEnv, - TSqlHDBC: SqlHDBC, TSqlHStmt: SqlHStmt, TSqlHDesc: SqlHDesc, - # TSqlInteger: TSqlInteger, # Name conflict if we drop `T` - TSqlUInteger: SqlUInteger, TSqlPointer: SqlPointer, - # TSqlReal: TSqlReal, # Name conflict if we drop`T` - # TSqlDouble: TSqlDouble, # Name conflict if we drop`T` - # TSqlFloat: TSqlFloat, # Name conflict if we drop `T` - TSqlHWND: SqlHWND].} const # SQL data type codes SQL_UNKNOWN_TYPE* = 0 diff --git a/lib/wrappers/openssl.nim b/lib/wrappers/openssl.nim index e1d36e461..2f072d5c7 100644 --- a/lib/wrappers/openssl.nim +++ b/lib/wrappers/openssl.nim @@ -38,7 +38,7 @@ when useWinVersion: from winlean import SocketHandle else: - const versions = "(.1.1|.38|.39|.41|.43|.44|.45|.10|.1.0.2|.1.0.1|.1.0.0|.0.9.9|.0.9.8|)" + const versions = "(.1.1|.38|.39|.41|.43|.44|.45|.46|.10|.1.0.2|.1.0.1|.1.0.0|.0.9.9|.0.9.8|)" when defined(macosx): const @@ -83,8 +83,6 @@ type pem_password_cb* = proc(buf: cstring, size, rwflag: cint, userdata: pointer): cint {.cdecl.} -{.deprecated: [PSSL: SslPtr, PSSL_CTX: SslCtx, PBIO: BIO].} - const SSL_SENT_SHUTDOWN* = 1 SSL_RECEIVED_SHUTDOWN* = 2 diff --git a/lib/wrappers/pcre.nim b/lib/wrappers/pcre.nim index e9e11960c..c4bb24cfd 100644 --- a/lib/wrappers/pcre.nim +++ b/lib/wrappers/pcre.nim @@ -466,14 +466,6 @@ proc study*(code: ptr Pcre, {.pop.} -{.deprecated: [MAJOR: PCRE_MAJOR, MINOR: PCRE_MINOR, - PRERELEASE: PCRE_PRERELEASE, DATE: PCRE_DATE].} - -{.deprecated: [TPcre: Pcre, TJitStack: JitStack].} type PPcre* {.deprecated.} = ptr Pcre PJitStack* {.deprecated.} = ptr JitStack - -{.deprecated: [TExtra: ExtraData].} -{.deprecated: [TCalloutBlock: CalloutBlock].} -{.deprecated: [TJitCallback: JitCallback].} diff --git a/lib/wrappers/postgres.nim b/lib/wrappers/postgres.nim index 7cb816f68..f38712c0e 100644 --- a/lib/wrappers/postgres.nim +++ b/lib/wrappers/postgres.nim @@ -152,10 +152,6 @@ type length*: int32 isint*: int32 p*: pointer -{.deprecated: [TSockAddr: SockAddr, TPGresAttDesc: PgresAttDesc, - TPGresAttValue: PgresAttValue, TExecStatusType: ExecStatusType, - TPGlobjfuncs: Pglobjfuncs, TConnStatusType: ConnStatusType, TPGconn: Pgconn, - TPGresult: PGresult].} proc pqconnectStart*(conninfo: cstring): PPGconn{.cdecl, dynlib: dllName, importc: "PQconnectStart".} diff --git a/lib/wrappers/sqlite3.nim b/lib/wrappers/sqlite3.nim index 0276a0a65..e4bf2e67b 100644 --- a/lib/wrappers/sqlite3.nim +++ b/lib/wrappers/sqlite3.nim @@ -121,12 +121,6 @@ type para4: int32, para5: pointer): int32{.cdecl.} Collation_needed_func* = proc (para1: pointer, para2: PSqlite3, eTextRep: int32, para4: cstring){.cdecl.} -{.deprecated: [TSqlite3: Sqlite3, TContext: Context, Tvalue: Value, - Tcallback: Callback, Tcreate_function_step_func: Create_function_step_func, - Tcreate_function_func_func: Create_function_func_func, - Tcreate_function_final_func: Create_function_final_func, - Tresult_func: Result_func, Tcreate_collation_func: Create_collation_func, - Tcollation_needed_func: Collation_needed_func].} const SQLITE_STATIC* = nil diff --git a/lib/wrappers/tinyc.nim b/lib/wrappers/tinyc.nim index f2ce92d36..f29758f13 100644 --- a/lib/wrappers/tinyc.nim +++ b/lib/wrappers/tinyc.nim @@ -12,7 +12,6 @@ type PccState* = ptr CcState ErrorFunc* = proc (opaque: pointer, msg: cstring) {.cdecl.} -{.deprecated: [TccState: CcState, TErrorFunc: ErrorFunc].} proc openCCState*(): PccState {.importc: "tcc_new", cdecl.} ## create a new TCC compilation context diff --git a/nimdoc/testproject/expected/subdir/subdir_b/utils.html b/nimdoc/testproject/expected/subdir/subdir_b/utils.html index 60050d8ae..2c89acce4 100644 --- a/nimdoc/testproject/expected/subdir/subdir_b/utils.html +++ b/nimdoc/testproject/expected/subdir/subdir_b/utils.html @@ -16,7 +16,7 @@ <link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/> <!-- CSS --> -<title>Module utils</title> +<title>utils</title> <style type="text/css" > /* Stylesheet for use with Docutils/rst2html. @@ -1219,7 +1219,7 @@ function main() { <body onload="main()"> <div class="document" id="documentId"> <div class="container"> - <h1 class="title">Module utils</h1> + <h1 class="title">utils</h1> <div class="row"> <div class="three columns"> <div id="global-links"> diff --git a/nimdoc/testproject/expected/testproject.html b/nimdoc/testproject/expected/testproject.html index 83de0d894..2e23a64d5 100644 --- a/nimdoc/testproject/expected/testproject.html +++ b/nimdoc/testproject/expected/testproject.html @@ -16,7 +16,7 @@ <link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/> <!-- CSS --> -<title>Module testproject</title> +<title>testproject</title> <style type="text/css" > /* Stylesheet for use with Docutils/rst2html. @@ -1219,7 +1219,7 @@ function main() { <body onload="main()"> <div class="document" id="documentId"> <div class="container"> - <h1 class="title">Module testproject</h1> + <h1 class="title">testproject</h1> <div class="row"> <div class="three columns"> <div id="global-links"> diff --git a/nimpretty/nimpretty.nim b/nimpretty/nimpretty.nim index fb2b8637f..628bc163e 100644 --- a/nimpretty/nimpretty.nim +++ b/nimpretty/nimpretty.nim @@ -12,7 +12,8 @@ when not defined(nimpretty): {.error: "This needs to be compiled with --define:nimPretty".} -import ../compiler / [idents, msgs, ast, syntaxes, renderer, options, pathutils] +import ../compiler / [idents, msgs, ast, syntaxes, renderer, options, + pathutils, layouter] import parseopt, strutils, os @@ -26,6 +27,7 @@ Usage: Options: --backup:on|off create a backup file before overwritting (default: ON) --output:file set the output file (default: overwrite the .nim file) + --indent:N set the number of spaces that is used for indentation --version show the version --help show this help """ @@ -40,12 +42,20 @@ proc writeVersion() = stdout.flushFile() quit(0) -proc prettyPrint(infile, outfile: string) = +type + PrettyOptions = object + indWidth: int + +proc prettyPrint(infile, outfile: string, opt: PrettyOptions) = var conf = newConfigRef() let fileIdx = fileInfoIdx(conf, AbsoluteFile infile) conf.outFile = AbsoluteFile outfile when defined(nimpretty2): - discard parseFile(fileIdx, newIdentCache(), conf) + var p: TParsers + p.parser.em.indWidth = opt.indWidth + if setupParsers(p, fileIdx, newIdentCache(), conf): + discard parseAll(p) + closeParsers(p) else: let tree = parseFile(fileIdx, newIdentCache(), conf) renderModule(tree, infile, outfile, {}, fileIdx, conf) @@ -53,6 +63,7 @@ proc prettyPrint(infile, outfile: string) = proc main = var infile, outfile: string var backup = true + var opt: PrettyOptions for kind, key, val in getopt(): case kind of cmdArgument: @@ -63,6 +74,7 @@ proc main = of "version", "v": writeVersion() of "backup": backup = parseBool(val) of "output", "o": outfile = val + of "indent": opt.indWidth = parseInt(val) else: writeHelp() of cmdEnd: assert(false) # cannot happen if infile.len == 0: @@ -70,6 +82,6 @@ proc main = if backup: os.copyFile(source=infile, dest=changeFileExt(infile, ".nim.backup")) if outfile.len == 0: outfile = infile - prettyPrint(infile, outfile) + prettyPrint(infile, outfile, opt) main() diff --git a/nimpretty/tests/exhaustive.nim b/nimpretty/tests/exhaustive.nim index 9202de703..9a0f9a49b 100644 --- a/nimpretty/tests/exhaustive.nim +++ b/nimpretty/tests/exhaustive.nim @@ -357,3 +357,6 @@ proc fun3() = ##[ foobar ]## + +# bug #9673 +discard `* `(1, 2) diff --git a/nimpretty/tests/expected/exhaustive.nim b/nimpretty/tests/expected/exhaustive.nim index 6d000d524..8ea7ca4dd 100644 --- a/nimpretty/tests/expected/exhaustive.nim +++ b/nimpretty/tests/expected/exhaustive.nim @@ -366,3 +366,6 @@ proc fun3() = ##[ foobar ]## + +# bug #9673 +discard `*`(1, 2) diff --git a/nimsuggest/nimsuggest.nim b/nimsuggest/nimsuggest.nim index 34b1cc4f7..89b5bf4ed 100644 --- a/nimsuggest/nimsuggest.nim +++ b/nimsuggest/nimsuggest.nim @@ -186,7 +186,7 @@ proc execute(cmd: IdeCmd, file, dirtyfile: AbsoluteFile, line, col: int; if conf.suggestVersion == 1: graph.usageSym = nil if not isKnownFile: - graph.compileProject() + graph.compileProject(dirtyIdx) if conf.suggestVersion == 0 and conf.ideCmd in {ideUse, ideDus} and dirtyfile.isEmpty: discard "no need to recompile anything" @@ -195,7 +195,8 @@ proc execute(cmd: IdeCmd, file, dirtyfile: AbsoluteFile, line, col: int; graph.markDirty dirtyIdx graph.markClientsDirty dirtyIdx if conf.ideCmd != ideMod: - graph.compileProject(modIdx) + if isKnownFile: + graph.compileProject(modIdx) if conf.ideCmd in {ideUse, ideDus}: let u = if conf.suggestVersion != 1: graph.symFromInfo(conf.m.trackPos) else: graph.usageSym if u != nil: @@ -613,4 +614,103 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) = discard self.loadConfigsAndRunMainCommand(cache, conf) -handleCmdline(newIdentCache(), newConfigRef()) +when isMainModule: + handleCmdline(newIdentCache(), newConfigRef()) +else: + export Suggest + export IdeCmd + export AbsoluteFile + type NimSuggest* = ref object + graph: ModuleGraph + idle: int + cachedMsgs: CachedMsgs + + proc initNimSuggest*(project: string, nimPath: string = ""): NimSuggest = + var retval: ModuleGraph + proc mockCommand(graph: ModuleGraph) = + retval = graph + let conf = graph.config + clearPasses(graph) + registerPass graph, verbosePass + registerPass graph, semPass + conf.cmd = cmdIdeTools + wantMainModule(conf) + + if not fileExists(conf.projectFull): + quit "cannot find file: " & conf.projectFull.string + + add(conf.searchPaths, conf.libpath) + + # do not stop after the first error: + conf.errorMax = high(int) + # do not print errors, but log them + conf.writelnHook = proc (s: string) = log(s) + conf.structuredErrorHook = nil + + # compile the project before showing any input so that we already + # can answer questions right away: + compileProject(graph) + + + proc mockCmdLine(pass: TCmdLinePass, cmd: string; conf: ConfigRef) = + conf.suggestVersion = 0 + let a = unixToNativePath(project) + if dirExists(a) and not fileExists(a.addFileExt("nim")): + conf.projectName = findProjectNimFile(conf, a) + # don't make it worse, report the error the old way: + if conf.projectName.len == 0: conf.projectName = a + else: + conf.projectName = a + # if processArgument(pass, p, argsCount): break + let + cache = newIdentCache() + conf = newConfigRef() + self = NimProg( + suggestMode: true, + processCmdLine: mockCmdLine, + mainCommand: mockCommand + ) + self.initDefinesProg(conf, "nimsuggest") + + self.processCmdLineAndProjectPath(conf) + + if gMode != mstdin: + conf.writelnHook = proc (msg: string) = discard + # Find Nim's prefix dir. + if nimPath == "": + let binaryPath = findExe("nim") + if binaryPath == "": + raise newException(IOError, + "Cannot find Nim standard library: Nim compiler not in PATH") + conf.prefixDir = AbsoluteDir binaryPath.splitPath().head.parentDir() + if not dirExists(conf.prefixDir / RelativeDir"lib"): + conf.prefixDir = AbsoluteDir"" + else: + conf.prefixDir = AbsoluteDir nimPath + + #msgs.writelnHook = proc (line: string) = log(line) + myLog("START " & conf.projectFull.string) + + discard self.loadConfigsAndRunMainCommand(cache, conf) + if gLogging: + for it in conf.searchPaths: + log(it.string) + + retval.doStopCompile = proc (): bool = false + return NimSuggest(graph: retval, idle: 0, cachedMsgs: @[]) + + proc runCmd*(nimsuggest: NimSuggest, cmd: IdeCmd, file, dirtyfile: AbsoluteFile, line, col: int): seq[Suggest] = + var retval: seq[Suggest] = @[] + let conf = nimsuggest.graph.config + conf.ideCmd = cmd + conf.writelnHook = proc (line: string) = + retval.add(Suggest(section: ideMsg, doc: line)) + conf.suggestionResultHook = proc (s: Suggest) = + retval.add(s) + if conf.ideCmd == ideKnown: + retval.add(Suggest(section: ideKnown, quality: ord(fileInfoKnown(conf, file)))) + else: + if conf.ideCmd == ideChk: + for cm in nimsuggest.cachedMsgs: errorHook(conf, cm.info, cm.msg, cm.sev) + execute(conf.ideCmd, file, dirtyfile, line, col, nimsuggest.graph) + return retval diff --git a/nimsuggest/nimsuggest.nim.cfg b/nimsuggest/nimsuggest.nim.cfg index 820db0dba..394449740 100644 --- a/nimsuggest/nimsuggest.nim.cfg +++ b/nimsuggest/nimsuggest.nim.cfg @@ -22,5 +22,3 @@ define:nimcore #define:noDocgen --path:"$nim" --threads:on ---noNimblePath ---path:"../compiler" diff --git a/testament/categories.nim b/testament/categories.nim index 36f2a271a..e1f173c26 100644 --- a/testament/categories.nim +++ b/testament/categories.nim @@ -97,11 +97,10 @@ proc runBasicDLLTest(c, r: var TResults, cat: Category, options: string) = else: "" - testSpec c, makeTest("lib/nimrtl.nim", - options & " --app:lib -d:createNimRtl --threads:on", cat) - testSpec c, makeTest("tests/dll/server.nim", - options & " --app:lib -d:useNimRtl --threads:on" & rpath, cat) - + testNoSpec c, makeTest("lib/nimrtl.nim", + options & " --app:lib -d:createNimRtl --threads:on", cat, actionCompile) + testNoSpec c, makeTest("tests/dll/server.nim", + options & " --app:lib -d:useNimRtl --threads:on" & rpath, cat, actionCompile) when defined(Windows): # windows looks in the dir of the exe (yay!): @@ -298,25 +297,24 @@ proc testNimInAction(r: var TResults, cat: Category, options: string) = # whoever edits these hashes without dom96's permission, j/k. But please only # edit when making a conscious breaking change, also please try to make your # commit message clear and notify me so I can easily compile an errata later. - var testHashes: seq[string] = @[] - - for test in tests: - testHashes.add(getMD5(readFile("tests" / test.addFileExt("nim")).string)) - const refHashes = @[ "51afdfa84b3ca3d810809d6c4e5037ba", "30f07e4cd5eaec981f67868d4e91cfcf", "d14e7c032de36d219c9548066a97e846", "2e40bfd5daadb268268727da91bb4e81", "c5d3853ed0aba04bf6d35ba28a98dca0", "058603145ff92d46c009006b06e5b228", "7b94a029b94ddb7efafddd546c965ff6", "586d74514394e49f2370dfc01dd9e830", - "e1901837b757c9357dc8d259fd0ef0f6", "097670c7ae12e825debaf8ec3995227b", - "a8cb7b78cc78d28535ab467361db5d6e", "bfaec2816a1848991b530c1ad17a0184", - "47cb71bb4c1198d6d29cdbee05aa10b9", "87e4436809f9d73324cfc4f57f116770", - "7b7db5cddc8cf8fa9b6776eef1d0a31d", "e6e40219f0f2b877869b738737b7685e", - "6532ee87d819f2605a443d5e94f9422a", "9a8fe78c588d08018843b64b57409a02", - "03a801275b8b76b4170c870cd0da079d", "20bb7d3e2d38d43b0cb5fcff4909a4a8", - "af6844598f534fab6942abfa4dfe9ab2", "2a7a17f84f6503d9bc89a5ab8feea127" + "13febc363ed82585f2a60de40ddfefda", "c11a013db35e798f44077bc0763cc86d", + "3e32e2c5e9a24bd13375e1cd0467079c", "0b9fe7ba159623d49ae60db18a15037c", + "b2dd5293d7f784824bbf9792c6fb51ad", "4c19d8d9026bfe151b31d7007fa3c237", + "9415c6a568cfceed08da8378e95b5cd5", "da520038c153f4054cb8cc5faa617714", + "e6c6e061b6f77b2475db6fec7abfb7f4", "9a8fe78c588d08018843b64b57409a02", + "8b5d28e985c0542163927d253a3e4fc9", "783299b98179cc725f9c46b5e3b5381f", + "bc523f9a9921299090bac1af6c958e73", "80f9c3e594a798225046e8a42e990daf" ] - doAssert testHashes == refHashes, "Nim in Action tests were changed." + + for i, test in tests: + let filename = "tests" / test.addFileExt("nim") + let testHash = getMD5(readFile(filename).string) + doAssert testHash == refHashes[i], "Nim in Action test " & filename & " was changed." # Run the tests. for testfile in tests: @@ -329,8 +327,6 @@ proc testNimInAction(r: var TResults, cat: Category, options: string) = testCPP cppFile - - # ------------------------- manyloc ------------------------------------------- #proc runSpecialTests(r: var TResults, options: string) = # for t in ["lib/packages/docutils/highlite"]: diff --git a/testament/specs.nim b/testament/specs.nim index c51a3343e..8afe9d98e 100644 --- a/testament/specs.nim +++ b/testament/specs.nim @@ -10,20 +10,21 @@ import parseutils, strutils, os, osproc, streams, parsecfg -var compilerPrefix* = "compiler" / "nim " +var compilerPrefix* = "compiler" / "nim" let isTravis* = existsEnv("TRAVIS") let isAppVeyor* = existsEnv("APPVEYOR") proc cmdTemplate*(): string = - compilerPrefix & "$target --lib:lib --hints:on -d:testing --nimblePath:tests/deps $options $file" + compilerPrefix & " $target --lib:lib --hints:on -d:testing --nimblePath:tests/deps $options $file" type TTestAction* = enum - actionCompile = "compile" actionRun = "run" + actionCompile = "compile" actionReject = "reject" actionRunNoSpec = "runNoSpec" + TResultEnum* = enum reNimcCrash, # nim compiler seems to have crashed reMsgsDiffer, # error messages differ @@ -39,6 +40,7 @@ type reBuildFailed # package building failed reIgnored, # test is ignored reSuccess # test was successful + TTarget* = enum targetC = "C" targetCpp = "C++" @@ -48,6 +50,7 @@ type TSpec* = object action*: TTestAction file*, cmd*: string + input*: string outp*: string line*, column*: int tfile*: string @@ -144,6 +147,8 @@ proc parseSpec*(filename: string): TSpec = of "output": result.action = actionRun result.outp = e.value + of "input": + result.input = e.value of "outputsub": result.action = actionRun result.outp = e.value @@ -186,7 +191,7 @@ proc parseSpec*(filename: string): TSpec = raise newException(ValueError, "cannot interpret as a bool: " & e.value) of "cmd": if e.value.startsWith("nim "): - result.cmd = compilerPrefix & e.value[4..^1] + result.cmd = compilerPrefix & e.value[3..^1] else: result.cmd = e.value of "ccodecheck": result.ccodeCheck = e.value diff --git a/testament/tester.nim b/testament/tester.nim index 169210255..2a8d0f5c8 100644 --- a/testament/tester.nim +++ b/testament/tester.nim @@ -74,6 +74,33 @@ proc getFileDir(filename: string): string = if not result.isAbsolute(): result = getCurrentDir() / result +proc execCmdEx2(command: string, args: openarray[string], options: set[ProcessOption], input: string): tuple[ + output: TaintedString, + exitCode: int] {.tags: + [ExecIOEffect, ReadIOEffect, RootEffect], gcsafe.} = + var p = startProcess(command, args=args, options=options) + var outp = outputStream(p) + + # There is no way to provide input for the child process + # anymore. Closing it will create EOF on stdin instead of eternal + # blocking. + let instream = inputStream(p) + instream.write(input) + close instream + + result = (TaintedString"", -1) + var line = newStringOfCap(120).TaintedString + while true: + if outp.readLine(line): + result[0].string.add(line.string) + result[0].string.add("\n") + else: + result[1] = peekExitCode(p) + if result[1] != -1: break + close(p) + + + proc nimcacheDir(filename, options: string, target: TTarget): string = ## Give each test a private nimcache dir so they don't clobber each other's. let hashInput = options & $target @@ -168,7 +195,7 @@ proc addResult(r: var TResults, test: TTest, target: TTarget, expected, given: string, success: TResultEnum) = let name = test.name.extractFilename & " " & $target & test.options let duration = epochTime() - test.startTime - let durationStr = duration.formatFloat(ffDecimal, precision = 8) + let durationStr = duration.formatFloat(ffDecimal, precision = 8).align(11) backend.writeTestResult(name = name, category = test.cat.string, target = $target, @@ -358,8 +385,16 @@ proc testSpec(r: var TResults, test: TTest, target = targetC) = reExeNotFound) continue - let exeCmd = (if isJsTarget: nodejs & " " else: "") & exeFile - var (buf, exitCode) = execCmdEx(exeCmd, options = {poStdErrToStdOut}) + + var exeCmd: string + var args: seq[string] + if isJsTarget: + exeCmd = nodejs + args.add exeFile + else: + exeCmd = exeFile + + var (buf, exitCode) = execCmdEx2(exeCmd, args, options = {poStdErrToStdOut}, input = expected.input) # Treat all failure codes from nodejs as 1. Older versions of nodejs used # to return other codes, but for us it is sufficient to know that it's not 0. @@ -469,7 +504,7 @@ proc main() = of "targets": targetsStr = p.val.string targets = parseTargets(targetsStr) - of "nim": compilerPrefix = p.val.string & " " + of "nim": compilerPrefix = p.val.string else: quit Usage p.next() if p.kind != cmdArgument: quit Usage diff --git a/tests/array/tarray.nim b/tests/array/tarray.nim index 4a31a4d6d..8551d324c 100644 --- a/tests/array/tarray.nim +++ b/tests/array/tarray.nim @@ -400,7 +400,7 @@ block troofregression: block tunchecked: {.boundchecks: on.} - type Unchecked {.unchecked.} = array[0, char] + type Unchecked = UncheckedArray[char] var x = cast[ptr Unchecked](alloc(100)) x[5] = 'x' diff --git a/tests/assert/testhelper.nim b/tests/assert/testhelper.nim index 754d562ec..2e5ede990 100644 --- a/tests/assert/testhelper.nim +++ b/tests/assert/testhelper.nim @@ -1,5 +1,5 @@ from strutils import endsWith, split -from ospaths import isAbsolute +from os import isAbsolute proc checkMsg*(msg, expectedEnd, name: string)= let filePrefix = msg.split(' ', maxSplit = 1)[0] diff --git a/tests/async/t7192.nim b/tests/async/t7192.nim new file mode 100644 index 000000000..c380f93e1 --- /dev/null +++ b/tests/async/t7192.nim @@ -0,0 +1,14 @@ +discard """ +output: ''' +testCallback() +''' +""" + +import asyncdispatch + +proc testCallback() = + echo "testCallback()" + +when isMainModule: + callSoon(testCallback) + poll() diff --git a/tests/async/t8982.nim b/tests/async/t8982.nim new file mode 100644 index 000000000..5face7edf --- /dev/null +++ b/tests/async/t8982.nim @@ -0,0 +1,33 @@ +discard """ +output: ''' +timeout +runForever should throw ValueError, this is expected +''' +""" + + +import asyncdispatch + +proc failingAwaitable(p: int) {.async.} = + await sleepAsync(500) + if p > 0: + raise newException(Exception, "my exception") + +proc main() {.async.} = + let fut = failingAwaitable(1) + try: + await fut or sleepAsync(100) + if fut.finished: + echo "finished" + else: + echo "timeout" + except: + echo "failed" + + +# Previously this would raise "An attempt was made to complete a Future more than once." +try: + asyncCheck main() + runForever() +except ValueError: + echo "runForever should throw ValueError, this is expected" diff --git a/tests/async/tasyncall.nim b/tests/async/tasyncall.nim index 775dd0c6f..3e30e8ba8 100644 --- a/tests/async/tasyncall.nim +++ b/tests/async/tasyncall.nim @@ -2,7 +2,7 @@ discard """ file: "tasyncall.nim" exitcode: 0 """ -import times, sequtils, unittest +import times, sequtils import asyncdispatch const diff --git a/tests/async/tasyncssl.nim b/tests/async/tasyncssl.nim index 0607cf3c6..212260922 100644 --- a/tests/async/tasyncssl.nim +++ b/tests/async/tasyncssl.nim @@ -2,7 +2,13 @@ discard """ file: "tasyncssl.nim" cmd: "nim $target --hints:on --define:ssl $options $file" output: "500" + disabled: "windows" + target: c + action: compile """ + +# XXX, deactivated + import asyncdispatch, asyncnet, net, strutils, os when defined(ssl): @@ -62,5 +68,3 @@ when defined(ssl): assert msgCount == swarmSize * messagesToSend echo msgCount - - diff --git a/tests/async/tcallbacks.nim b/tests/async/tcallbacks.nim index 8c08032cd..bd82d5824 100644 --- a/tests/async/tcallbacks.nim +++ b/tests/async/tcallbacks.nim @@ -1,8 +1,9 @@ discard """ exitcode: 0 - output: '''3 -2 + output: ''' 1 +2 +3 5 ''' """ diff --git a/tests/async/tfuturevar.nim b/tests/async/tfuturevar.nim index ea2c63e03..9e3134261 100644 --- a/tests/async/tfuturevar.nim +++ b/tests/async/tfuturevar.nim @@ -1,3 +1,8 @@ +discard """ +action: compile +""" +# XXX: action should be run! + import asyncdispatch proc completeOnReturn(fut: FutureVar[string], x: bool) {.async.} = @@ -44,4 +49,3 @@ proc main() {.async.} = waitFor main() - diff --git a/tests/casestmt/tlinearscanend.nim b/tests/casestmt/tlinearscanend.nim index 9a984e039..96e3727d5 100644 --- a/tests/casestmt/tlinearscanend.nim +++ b/tests/casestmt/tlinearscanend.nim @@ -1,3 +1,6 @@ +discard """ +action: compile +""" import strutils @@ -21,4 +24,3 @@ of 21: echo "21" else: {.linearScanEnd.} echo "default" - diff --git a/tests/ccgbugs/t6756.nim b/tests/ccgbugs/t6756.nim index 0f08557eb..5170a99f4 100644 --- a/tests/ccgbugs/t6756.nim +++ b/tests/ccgbugs/t6756.nim @@ -1,3 +1,9 @@ +discard """ +output: ''' +(v: 3) +''' +""" + import typetraits type A[T] = ref object diff --git a/tests/ccgbugs/t8964.nim b/tests/ccgbugs/t8964.nim deleted file mode 100644 index 5b41e8bdb..000000000 --- a/tests/ccgbugs/t8964.nim +++ /dev/null @@ -1,10 +0,0 @@ -discard """ - targets: "c cpp" -""" - -from json import JsonParsingError -import marshal - -const nothing = "" -doAssertRaises(JsonParsingError): - var bar = marshal.to[int](nothing) diff --git a/tests/ccgbugs/tcodegendecllambda.nim b/tests/ccgbugs/tcodegendecllambda.nim index 6dce68db5..5c6608b77 100644 --- a/tests/ccgbugs/tcodegendecllambda.nim +++ b/tests/ccgbugs/tcodegendecllambda.nim @@ -1,6 +1,7 @@ discard """ targets: "c cpp js" ccodecheck: "'HELLO'" + action: compile """ when defined(JS): diff --git a/tests/ccgbugs/tgeneric_closure.nim b/tests/ccgbugs/tgeneric_closure.nim index f9d5e7910..bb3b924b9 100644 --- a/tests/ccgbugs/tgeneric_closure.nim +++ b/tests/ccgbugs/tgeneric_closure.nim @@ -1,4 +1,10 @@ - +discard """ +output: ''' +2 +2 +2 +''' +""" # bug 2659 diff --git a/tests/ccgbugs/trecursive_closure.nim b/tests/ccgbugs/trecursive_closure.nim index f64382a8c..4b6514b90 100644 --- a/tests/ccgbugs/trecursive_closure.nim +++ b/tests/ccgbugs/trecursive_closure.nim @@ -1,3 +1,7 @@ +discard """ +action: compile +""" + # bug #2233 type MalType = object fun: proc: MalType diff --git a/tests/ccgbugs/tsighash_typename_regression.nim b/tests/ccgbugs/tsighash_typename_regression.nim index 7122902d9..6e49bafc3 100644 --- a/tests/ccgbugs/tsighash_typename_regression.nim +++ b/tests/ccgbugs/tsighash_typename_regression.nim @@ -1,3 +1,10 @@ +discard """ +output: ''' +123 +baz +''' +""" + # bug #5147 proc foo[T](t: T) = diff --git a/tests/ccgbugs/tuple_canon.nim b/tests/ccgbugs/tuple_canon.nim index 7e9e91836..671986054 100644 --- a/tests/ccgbugs/tuple_canon.nim +++ b/tests/ccgbugs/tuple_canon.nim @@ -1,4 +1,9 @@ - +discard """ +output: ''' +vidx 18 +0,0 +''' +""" # bug #4626 var foo: (int, array[1, int]) # Tuple must be of length > 1 diff --git a/tests/closure/tclosure.nim b/tests/closure/tclosure.nim index 141968462..c213d6a4b 100644 --- a/tests/closure/tclosure.nim +++ b/tests/closure/tclosure.nim @@ -1,5 +1,5 @@ discard """ - file: "tclosure.nim" + target: "c" output: ''' 1 3 6 11 20 foo foo88 diff --git a/tests/closure/texplicit_dummy_closure.nim b/tests/closure/texplicit_dummy_closure.nim index ec608b31a..9cd8c8ca9 100644 --- a/tests/closure/texplicit_dummy_closure.nim +++ b/tests/closure/texplicit_dummy_closure.nim @@ -5,8 +5,8 @@ import os type Window = object - oneInstSock*: PAsyncSocket - IODispatcher*: PDispatcher + oneInstSock*: AsyncSocket + IODispatcher*: Dispatcher var win: Window @@ -14,9 +14,9 @@ var proc initSocket() = win.oneInstSock = asyncSocket() #win.oneInstSock.handleAccept = - proc test(s: PAsyncSocket) = - var client: PAsyncSocket - proc dummy(c: PAsyncSocket) {.closure.} = + proc test(s: AsyncSocket) = + var client: AsyncSocket + proc dummy(c: AsyncSocket) {.closure.} = discard client.handleRead = dummy test(win.oneInstSock) diff --git a/tests/closure/tmacrobust1512.nim b/tests/closure/tmacrobust1512.nim index 5f13e8286..0f44c5e1a 100644 --- a/tests/closure/tmacrobust1512.nim +++ b/tests/closure/tmacrobust1512.nim @@ -1,8 +1,12 @@ +discard """ +output: "" +""" + import macros, strutils # https://github.com/nim-lang/Nim/issues/1512 -proc macrobust0(raw_input: string) = +proc macrobust0(input: string): string = var output = "" proc p1(a:string) = output.add(a) @@ -27,13 +31,9 @@ proc macrobust0(raw_input: string) = 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) @@ -53,11 +53,9 @@ proc macrobust0(raw_input: string) = p2(a) p1(a) + result = output - echo output - -macro macrobust(raw_input: untyped): untyped = - +macro macrobust(input: static[string]): untyped = var output = "" proc p1(a:string) = output.add(a) @@ -82,12 +80,9 @@ macro macrobust(raw_input: untyped): untyped = 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) @@ -105,11 +100,11 @@ macro macrobust(raw_input: untyped): untyped = p4(a) p3(a) p2(a) + p1(a) - echo output - discard result + result = newLit(output) -macrobust """ +const input = """ fdsasadfsdfa sadfsdafsdaf dsfsdafdsfadsfa fsdaasdfasdf fsdafsadfsad asdfasdfasdf @@ -122,16 +117,7 @@ macrobust """ sdfasdafsadf sdfasdafsdaf sdfasdafsdaf """ +let str1 = macrobust(input) +let str2 = macrobust0(input) -macrobust0 """ - fdsasadfsdfa sadfsdafsdaf - dsfsdafdsfadsfa fsdaasdfasdf - fsdafsadfsad asdfasdfasdf - fdsasdfasdfa sadfsadfsadf - sadfasdfsdaf sadfsdafsdaf dsfasdaf - sadfsdafsadf fdsasdafsadf fdsasadfsdaf - sdfasadfsdafdfsa sadfsadfsdaf - sdafsdaffsda sdfasadfsadf - fsdasdafsdfa sdfasdfafsda - sdfasdafsadf sdfasdafsdaf sdfasdafsdaf -""" +doAssert str1 == str2 diff --git a/tests/closure/ttimeinfo.nim b/tests/closure/ttimeinfo.nim index 3138ae72e..7416c0d31 100644 --- a/tests/closure/ttimeinfo.nim +++ b/tests/closure/ttimeinfo.nim @@ -1,15 +1,22 @@ +discard """ +output: ''' +@[2000-01-01T00:00:00+00:00, 2001-01-01T00:00:00+00:00, 2002-01-01T00:00:00+00:00, 2003-01-01T00:00:00+00:00, 2004-01-01T00:00:00+00:00, 2005-01-01T00:00:00+00:00, 2006-01-01T00:00:00+00:00, 2007-01-01T00:00:00+00:00, 2008-01-01T00:00:00+00:00, 2009-01-01T00:00:00+00:00, 2010-01-01T00:00:00+00:00, 2011-01-01T00:00:00+00:00, 2012-01-01T00:00:00+00:00, 2013-01-01T00:00:00+00:00, 2014-01-01T00:00:00+00:00, 2015-01-01T00:00:00+00:00] +@[2000-01-01T00:00:00+00:00, 2001-01-01T00:00:00+00:00, 2002-01-01T00:00:00+00:00, 2003-01-01T00:00:00+00:00, 2004-01-01T00:00:00+00:00, 2005-01-01T00:00:00+00:00, 2006-01-01T00:00:00+00:00, 2007-01-01T00:00:00+00:00, 2008-01-01T00:00:00+00:00, 2009-01-01T00:00:00+00:00, 2010-01-01T00:00:00+00:00, 2011-01-01T00:00:00+00:00, 2012-01-01T00:00:00+00:00, 2013-01-01T00:00:00+00:00, 2014-01-01T00:00:00+00:00, 2015-01-01T00:00:00+00:00] +''' +""" + # bug #2073 import sequtils import times # 1 -proc f(n: int): TimeInfo = - TimeInfo(year: n, month: mJan, monthday: 1) +proc f(n: int): DateTime = + DateTime(year: n, month: mJan, monthday: 1) echo toSeq(2000 || 2015).map(f) # 2 -echo toSeq(2000 || 2015).map(proc (n: int): TimeInfo = - TimeInfo(year: n, month: mJan, monthday: 1) +echo toSeq(2000 || 2015).map(proc (n: int): DateTime = + DateTime(year: n, month: mJan, monthday: 1) ) diff --git a/tests/compiles/trecursive_generic_in_compiles.nim b/tests/compiles/trecursive_generic_in_compiles.nim index 9c7fd10b3..c435ebaac 100644 --- a/tests/compiles/trecursive_generic_in_compiles.nim +++ b/tests/compiles/trecursive_generic_in_compiles.nim @@ -1,3 +1,7 @@ +discard """ +action: compile +""" + # bug #3313 import unittest, sugar {.experimental: "notnil".} diff --git a/tests/concepts/tconcepts_issues.nim b/tests/concepts/tconcepts_issues.nim index e145b9f37..df4037ffb 100644 --- a/tests/concepts/tconcepts_issues.nim +++ b/tests/concepts/tconcepts_issues.nim @@ -1,5 +1,4 @@ discard """ - file: "tissues.nim" output: ''' 20.0 USD Printable diff --git a/tests/concepts/tmapconcept.nim b/tests/concepts/tmapconcept.nim index 5082fcb61..6b959eff2 100644 --- a/tests/concepts/tmapconcept.nim +++ b/tests/concepts/tmapconcept.nim @@ -3,7 +3,7 @@ output: '''10 10 1''' -msg: ''' +nimout: ''' K=string V=int K=int64 V=string K=int V=int diff --git a/tests/concepts/tmatrixconcept.nim b/tests/concepts/tmatrixconcept.nim index dd5a080b6..ca31f5942 100644 --- a/tests/concepts/tmatrixconcept.nim +++ b/tests/concepts/tmatrixconcept.nim @@ -1,6 +1,6 @@ discard """ output: "0\n0\n0" -msg: ''' +nimout: ''' R=3 C=3 TE=9 FF=14 FC=20 T=int R=3 C=3 T=int ''' diff --git a/tests/concepts/tstackconcept.nim b/tests/concepts/tstackconcept.nim index cb8db566d..fb6032732 100644 --- a/tests/concepts/tstackconcept.nim +++ b/tests/concepts/tstackconcept.nim @@ -1,6 +1,6 @@ discard """ output: "20\n10" -msg: ''' +nimout: ''' INFERRED int VALUE TYPE int VALUE TYPE NAME INT diff --git a/tests/converter/t7098.nim b/tests/converter/t7098.nim index 66e629fa8..8e7634882 100644 --- a/tests/converter/t7098.nim +++ b/tests/converter/t7098.nim @@ -1,3 +1,7 @@ +discard """ +action: compile +""" + type Byte* = uint8 Bytes* = seq[Byte] diff --git a/tests/converter/tconvcolors.nim b/tests/converter/tconvcolors.nim index 07e829550..7b440cabd 100644 --- a/tests/converter/tconvcolors.nim +++ b/tests/converter/tconvcolors.nim @@ -1,5 +1,7 @@ +discard """ +output: "16777215A" +""" import colors echo int32(colWhite), 'A' - diff --git a/tests/converter/tconverter_unique_ptr.nim b/tests/converter/tconverter_unique_ptr.nim new file mode 100644 index 000000000..15ec609a3 --- /dev/null +++ b/tests/converter/tconverter_unique_ptr.nim @@ -0,0 +1,107 @@ + +discard """ + file: "tconverter_unique_ptr.nim" + targets: "c cpp" + output: '''5 +2.0 5 +''' +""" + +## Bugs 9698 and 9699 + +type + UniquePtr*[T] = object + ## non copyable pointer to object T, exclusive ownership of the object is assumed + val: ptr T + + MyLen* = distinct int + + MySeq* = object + ## Vectorized matrix + len: MyLen # scalar size + data: ptr UncheckedArray[float] + +proc `$`(x: MyLen): string {.borrow.} + +proc `=destroy`*(m: var MySeq) {.inline.} = + if m.data != nil: + deallocShared(m.data) + m.data = nil + +proc `=`*(m: var MySeq, m2: MySeq) = + if m.data == m2.data: return + if m.data != nil: + `=destroy`(m) + + m.len = m2.len + let bytes = m.len.int * sizeof(float) + if bytes > 0: + m.data = cast[ptr UncheckedArray[float]](allocShared(bytes)) + copyMem(m.data, m2.data, bytes) + +proc `=sink`*(m: var MySeq, m2: MySeq) {.inline.} = + if m.data != m2.data: + if m.data != nil: + `=destroy`(m) + m.len = m2.len + m.data = m2.data + +proc len*(m: MySeq): MyLen {.inline.} = m.len + +proc lenx*(m: var MySeq): MyLen {.inline.} = m.len + + +proc `[]`*(m: MySeq; i: MyLen): float {.inline.} = + m.data[i.int] + +proc `[]=`*(m: var MySeq; i: MyLen, val: float) {.inline.} = + m.data[i.int] = val + +proc setTo(s: var MySeq, val: float) = + for i in 0..<s.len.int: + s.data[i] = val + +proc newMySeq*(size: int, initial_value = 0.0): MySeq = + result.len = size.MyLen + if size > 0: + result.data = cast[ptr UncheckedArray[float]](createShared(float, size)) + + result.setTo(initial_value) + +converter literalToLen*(x: int{lit}): MyLen = + x.MyLen + + +#------------------------------------------------------------- +# Unique pointer implementation +#------------------------------------------------------------- + +proc `=destroy`*[T](p: var UniquePtr[T]) = + if p.val != nil: + `=destroy`(p.val[]) + dealloc(p.val) + p.val = nil + +proc `=`*[T](dest: var UniquePtr[T], src: UniquePtr[T]) {.error.} + +proc `=sink`*[T](dest: var UniquePtr[T], src: UniquePtr[T]) {.inline.} = + if dest.val != nil and dest.val != src.val: + `=destroy`(dest) + dest.val = src.val + +proc newUniquePtr*[T](val: sink T): UniquePtr[T] = + result.val = cast[type(result.val)](alloc(sizeof(result.val[]))) + reset(result.val[]) + result.val[] = val + +converter convertPtrToObj*[T](p: UniquePtr[T]): var T = + result = p.val[] + + +var pu = newUniquePtr(newMySeq(5, 1.0)) +echo pu.len + +pu[0] = 2.0 +echo pu[0], " ", pu.lenx + + diff --git a/tests/converter/tgenericconverter2.nim b/tests/converter/tgenericconverter2.nim index ae064d852..017651a6b 100644 --- a/tests/converter/tgenericconverter2.nim +++ b/tests/converter/tgenericconverter2.nim @@ -1,4 +1,36 @@ # bug #3799 +discard """ +output: ''' +00000000000000000000000000000000000000000 +00000000000001111111111111110000000000000 +00000000001111111111111111111110000000000 +00000000111111111111111111111111100000000 +00000011111222222221111111111111111000000 +00000111122222222222221111111111111100000 +00001112222333333459432111111111111110000 +00011122355544344463533221111111111111000 +00111124676667556896443322211111111111100 +00111126545561919686543322221111111111100 +01111123333346967807554322222211111111110 +01111122233334455582015332222221111111110 +01111122222333344567275432222222111111110 +01111112222222334456075443222222211111110 +01111111222222233459965444332222221111110 +01111111122222223457486554433322222111110 +01111111112222222367899655543333322111110 +01111111111122222344573948465444332111110 +00111111111112222334467987727667762111100 +00111111111111122233474655557836432111100 +00011111111111112233 454433334 4321111000 +00001111111111111122354333322222211110000 +00000111111111111111222222222222111100000 +00000001111111111111111122222111110000000 +00000000111111111111111111111111100000000 +00000000000111111111111111111100000000000 +00000000000000111111111111100000000000000 +''' +""" + import macros @@ -37,9 +69,9 @@ iterator stepIt[T](start, step: T, iterations: int): T = let c = (0.36237, 0.32) -for y in stepIt(2.0, -0.0375, 107): +for y in stepIt(2.0, -0.0375 * 4, 107 div 4): var row = "" - for x in stepIt(-2.0, 0.025, 160): + for x in stepIt(-2.0, 0.025 * 4, 160 div 4): #let n = julia((x, y), c, 4.0, nmax) ### this works let n = dendriteFractal((x, y), 4.0, nmax) if n < nmax: diff --git a/tests/coroutines/texceptions.nim b/tests/coroutines/texceptions.nim index f3debf0a7..323eb055d 100644 --- a/tests/coroutines/texceptions.nim +++ b/tests/coroutines/texceptions.nim @@ -1,3 +1,7 @@ +discard """ + target: "c" +""" + import coro var stackCheckValue = 1100220033 @@ -10,6 +14,7 @@ proc testExceptions(id: int, sleep: float) = numbers.add(id) raise (ref ValueError)() except: + suspend(sleep) numbers.add(id) suspend(sleep) numbers.add(id) @@ -18,6 +23,6 @@ proc testExceptions(id: int, sleep: float) = start(proc() = testExceptions(1, 0.01)) start(proc() = testExceptions(2, 0.011)) -run() +coro.run() doAssert(stackCheckValue == 1100220033, "Thread stack got corrupted") doAssert(numbers == @[1, 2, 1, 2, 1, 2, 1, 2, 1, 2], "Coroutines executed in incorrect order") diff --git a/tests/coroutines/tgc.nim b/tests/coroutines/tgc.nim index 66a12ab9d..46f4f8e83 100644 --- a/tests/coroutines/tgc.nim +++ b/tests/coroutines/tgc.nim @@ -1,3 +1,7 @@ +discard """ + target: "c" +""" + import coro var maxOccupiedMemory = 0 diff --git a/tests/coroutines/titerators.nim b/tests/coroutines/titerators.nim index e2623ce2d..abcfbde43 100644 --- a/tests/coroutines/titerators.nim +++ b/tests/coroutines/titerators.nim @@ -1,3 +1,7 @@ +discard """ + target: "c" +""" + import coro include system/timers @@ -18,7 +22,7 @@ var start = getTicks() start(proc() = theCoroutine(1, 0.01)) start(proc() = theCoroutine(2, 0.011)) run() + var executionTime = getTicks() - start -doAssert(executionTime >= 55_000_000.Nanos and executionTime < 56_000_000.Nanos, "Coroutines executed too short") doAssert(stackCheckValue == 1100220033, "Thread stack got corrupted") doAssert(numbers == @[10, 20, 11, 21, 12, 22, 13, 23, 14, 24], "Coroutines executed in incorrect order") diff --git a/tests/coroutines/twait.nim b/tests/coroutines/twait.nim index eb2765be4..1769966ab 100644 --- a/tests/coroutines/twait.nim +++ b/tests/coroutines/twait.nim @@ -1,6 +1,7 @@ discard """ output: "Exit 1\nExit 2" disabled: "macosx" + target: "c" """ import coro diff --git a/tests/cpp/t8241.nim b/tests/cpp/t8241.nim index cbee1d85a..9aed13fcb 100644 --- a/tests/cpp/t8241.nim +++ b/tests/cpp/t8241.nim @@ -20,4 +20,13 @@ proc findlib2: string = proc imported_func2*(a: cint): cstring {.importc, dynlib: findlib2().} echo imported_func(1) -echo imported_func2(1) \ No newline at end of file +echo imported_func2(1) + +# issue #8946 + +from json import JsonParsingError +import marshal + +const nothing = "" +doAssertRaises(JsonParsingError): + var bar = marshal.to[int](nothing) diff --git a/tests/cpp/tcasts.nim b/tests/cpp/tcasts.nim index 24ebb8f62..d968d87db 100644 --- a/tests/cpp/tcasts.nim +++ b/tests/cpp/tcasts.nim @@ -1,6 +1,6 @@ discard """ cmd: "nim cpp $file" - output: "" + output: '''{"vas": "kas", "123": "123"}''' targets: "cpp" """ @@ -9,3 +9,13 @@ block: #5979 var p: pointer = cast[pointer](a) var c = cast[char](p) doAssert(c == 'a') + + +#---------------------------------------------------- +# bug #9739 +import tables + +var t = initTable[string, string]() +discard t.hasKeyOrPut("123", "123") +discard t.mgetOrPut("vas", "kas") +echo t \ No newline at end of file diff --git a/tests/cpp/tnativesockets.nim b/tests/cpp/tnativesockets.nim index c62008050..1284811b5 100644 --- a/tests/cpp/tnativesockets.nim +++ b/tests/cpp/tnativesockets.nim @@ -1,5 +1,6 @@ discard """ targets: "cpp" +outputsub: "" """ import nativesockets diff --git a/tests/cpp/tsigbreak.nim b/tests/cpp/tsigbreak.nim index 9a381d84f..14d29adf7 100644 --- a/tests/cpp/tsigbreak.nim +++ b/tests/cpp/tsigbreak.nim @@ -1,5 +1,6 @@ discard """ targets: "cpp" + action: compile """ import tables, lists diff --git a/tests/defaultprocparam/tdefaultprocparam.nim b/tests/defaultprocparam/tdefaultprocparam.nim index 23ecf72e9..5f8c1adab 100644 --- a/tests/defaultprocparam/tdefaultprocparam.nim +++ b/tests/defaultprocparam/tdefaultprocparam.nim @@ -1,4 +1,8 @@ - +discard """ +output: ''' +hi +''' +""" import mdefaultprocparam p() diff --git a/tests/destructor/t6434.nim b/tests/destructor/t6434.nim index 9c912f1f9..c9ad213c2 100644 --- a/tests/destructor/t6434.nim +++ b/tests/destructor/t6434.nim @@ -1,21 +1,26 @@ discard """ exitcode: 0 - output: '''assingment -assingment -assingment -assingment -''' + output: "" """ type Foo* = object boo: int +var sink_counter = 0 +var assign_counter = 0 + +proc `=sink`(dest: var Foo, src: Foo) = + sink_counter.inc + proc `=`(dest: var Foo, src: Foo) = - debugEcho "assingment" + assign_counter.inc proc test(): auto = var a,b : Foo - return (a, b) + return (a, b, Foo(boo: 5)) + +var (a, b, _) = test() -var (a, b) = test() \ No newline at end of file +doAssert: assign_counter == 0 +doAssert: sink_counter == 9 \ No newline at end of file diff --git a/tests/destructor/texplicit_move.nim b/tests/destructor/texplicit_move.nim index 6735ac75d..230f0b133 100644 --- a/tests/destructor/texplicit_move.nim +++ b/tests/destructor/texplicit_move.nim @@ -2,7 +2,10 @@ discard """ output: '''3 0 -destroyed!''' +0 +10 +destroyed! +''' """ type @@ -17,3 +20,10 @@ var x.f = 3 echo move(x.f) echo x.f + +# bug #9743 +let a = create int +a[] = 10 +var b = move a[] +echo a[] +echo b diff --git a/tests/destructor/tmove_objconstr.nim b/tests/destructor/tmove_objconstr.nim index 50aecf46d..c6be2e7e3 100644 --- a/tests/destructor/tmove_objconstr.nim +++ b/tests/destructor/tmove_objconstr.nim @@ -60,3 +60,62 @@ for x in getPony(): # XXX this needs to be enabled once top level statements # produce destructor calls again. #echo "Pony is dying!" + + +#------------------------------------------------------------ +#-- Move into tuple constructor and move on tuple unpacking +#------------------------------------------------------------ + +type + MySeqNonCopyable* = object + len: int + data: ptr UncheckedArray[float] + +proc `=destroy`*(m: var MySeqNonCopyable) {.inline.} = + if m.data != nil: + deallocShared(m.data) + m.data = nil + +proc `=`*(m: var MySeqNonCopyable, m2: MySeqNonCopyable) {.error.} + +proc `=sink`*(m: var MySeqNonCopyable, m2: MySeqNonCopyable) {.inline.} = + if m.data != m2.data: + if m.data != nil: + `=destroy`(m) + m.len = m2.len + m.data = m2.data + +proc len*(m: MySeqNonCopyable): int {.inline.} = m.len + +proc `[]`*(m: MySeqNonCopyable; i: int): float {.inline.} = + m.data[i.int] + +proc `[]=`*(m: var MySeqNonCopyable; i, val: float) {.inline.} = + m.data[i.int] = val + +proc setTo(s: var MySeqNonCopyable, val: float) = + for i in 0..<s.len.int: + s.data[i] = val + +proc newMySeq*(size: int, initial_value = 0.0): MySeqNonCopyable =# + result.len = size + if size > 0: + result.data = cast[ptr UncheckedArray[float]](createShared(float, size)) + + result.setTo(initial_value) + +proc myfunc(x, y: int): (MySeqNonCopyable, MySeqNonCopyable) = + result = (newMySeq(x, 1.0), newMySeq(y, 5.0)) + +proc myfunc2(x, y: int): tuple[a: MySeqNonCopyable, b:int, c:MySeqNonCopyable] = + (a: newMySeq(x, 1.0), b:0, c:newMySeq(y, 5.0)) + +let (seq1, seq2) = myfunc(2, 3) +doAssert seq1.len == 2 +doAssert seq1[0] == 1.0 +doAssert seq2.len == 3 +doAssert seq2[0] == 5.0 + +var (seq3, i, _) = myfunc2(2, 3) +doAssert seq3.len == 2 +doAssert seq3[0] == 1.0 \ No newline at end of file diff --git a/tests/destructor/tuse_result_prevents_sinks.nim b/tests/destructor/tuse_result_prevents_sinks.nim new file mode 100644 index 000000000..4c32eac91 --- /dev/null +++ b/tests/destructor/tuse_result_prevents_sinks.nim @@ -0,0 +1,31 @@ +discard """ + output: "" +""" + +# bug #9594 + +type + Foo = object + i: int + +proc `=`(self: var Foo; other: Foo) = + self.i = other.i + 1 + +proc `=sink`(self: var Foo; other: Foo) = + self.i = other.i + +proc `=destroy`(self: var Foo) = discard + +proc test(): Foo = + result = Foo() + let temp = result + doAssert temp.i > 0 + return result + +proc testB(): Foo = + result = Foo() + let temp = result + doAssert temp.i > 0 + +discard test() +discard testB() diff --git a/tests/dir with space/tspace.nim b/tests/dir with space/tspace.nim index 2b74fa629..59237c9a1 100644 --- a/tests/dir with space/tspace.nim +++ b/tests/dir with space/tspace.nim @@ -1,3 +1,6 @@ -# Test for the compiler to be able to compile a Nim file with spaces in it. +discard """ +output: "Successful" +""" +# Test for the compiler to be able to compile a Nim file with spaces in the directory name. echo("Successful") diff --git a/tests/discard/tdiscardable.nim b/tests/discard/tdiscardable.nim index f979b29c9..a3dd966a0 100644 --- a/tests/discard/tdiscardable.nim +++ b/tests/discard/tdiscardable.nim @@ -1,3 +1,10 @@ +discard """ +output: ''' +1 +1 +''' +""" + # Test the discardable pragma proc p(x, y: int): int {.discardable.} = @@ -27,3 +34,11 @@ proc bar(b: int):int = echo foo(0) echo bar(0) + +# bug #9726 + +proc foo: (proc: int) = + proc bar: int = 1 + return bar + +discard foo() diff --git a/tests/discard/tillegaldiscard.nim b/tests/discard/tillegaldiscard.nim new file mode 100644 index 000000000..5e1a3f03e --- /dev/null +++ b/tests/discard/tillegaldiscard.nim @@ -0,0 +1,9 @@ +discard """ + line: 9 + errormsg: "illegal discard" +""" + +proc pop[T](arg: T): T = + echo arg + +discard tillegaldiscard.pop diff --git a/tests/dll/server.nim b/tests/dll/server.nim index e6b80df88..5ce1976ce 100644 --- a/tests/dll/server.nim +++ b/tests/dll/server.nim @@ -1,4 +1,5 @@ discard """ +action: compile cmd: "nim $target --debuginfo --hints:on --define:useNimRtl --app:lib $options $file" """ @@ -25,10 +26,3 @@ proc newOp(k: TNodeKind, a, b: PNode): PNode {.exportc: "newOp", dynlib.} = proc buildTree(x: int): PNode {.exportc: "buildTree", dynlib.} = result = newOp(nkMul, newOp(nkAdd, newLit(x), newLit(x)), newLit(x)) - -when false: - # Test the GC: - for i in 0..100_000: - discard buildTree(2) - - echo "Done" diff --git a/tests/effects/teffects6.nim b/tests/effects/teffects6.nim index 3dd83786f..6a4eea155 100644 --- a/tests/effects/teffects6.nim +++ b/tests/effects/teffects6.nim @@ -1,3 +1,8 @@ +discard """ +action: compile +""" + +# XXX: it is not actually tested if the effects are inferred type PMenu = ref object diff --git a/tests/errmsgs/treportunused.nim b/tests/errmsgs/treportunused.nim new file mode 100644 index 000000000..929da8843 --- /dev/null +++ b/tests/errmsgs/treportunused.nim @@ -0,0 +1,29 @@ +discard """ + nimout: ''' +treportunused.nim(19, 10) Hint: 'treportunused.s1(a: string)[declared in treportunused.nim(19, 9)]' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(26, 5) Hint: 's8' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(22, 6) Hint: 'treportunused.s4()[declared in treportunused.nim(22, 5)]' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(25, 7) Hint: 's7' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(24, 7) Hint: 's6' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(23, 6) Hint: 'treportunused.s5(a: T)[declared in treportunused.nim(23, 5)]' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(20, 10) Hint: 'treportunused.s2()[declared in treportunused.nim(20, 9)]' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(29, 6) Hint: 's11' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(27, 5) Hint: 's9' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(21, 10) Hint: 's3' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(28, 6) Hint: 's10' is declared but not used [XDeclaredButNotUsed] +''' +""" + +# bug #9764 + +iterator s1(a:string): int = discard +iterator s2(): int = discard +template s3(): untyped = 123 +proc s4(): int = 123 +proc s5[T](a: T): int = 123 +macro s6(a: int): untyped = discard +const s7 = 0 +let s8 = 0 +var s9: int +type s10 = object +type s11 = type(1.2) diff --git a/tests/exception/t9657.nim b/tests/exception/t9657.nim new file mode 100644 index 000000000..5d5164f4f --- /dev/null +++ b/tests/exception/t9657.nim @@ -0,0 +1,6 @@ +discard """ + action: run + exitcode: 1 +""" +close stdmsg +writeLine stdmsg, "exception!" diff --git a/tests/exception/tonraise.nim b/tests/exception/tonraise.nim deleted file mode 100644 index a155f0b8e..000000000 --- a/tests/exception/tonraise.nim +++ /dev/null @@ -1,34 +0,0 @@ -discard """ - output: '''i: 1 -success''' -""" - -type - ESomething = object of Exception - ESomeOtherErr = object of Exception - -proc genErrors(s: string) = - if s == "error!": - raise newException(ESomething, "Test") - else: - raise newException(EsomeotherErr, "bla") - -proc foo() = - var i = 0 - try: - inc i - onRaise(proc (e: ref Exception): bool = - echo "i: ", i) - genErrors("errssor!") - except ESomething: - echo("ESomething happened") - except: - echo("Some other error happened") - - # test that raise handler is gone: - try: - genErrors("error!") - except ESomething: - echo "success" - -foo() diff --git a/tests/exprs/tifexpr_typeinference.nim b/tests/exprs/tifexpr_typeinference.nim index 3ae95c571..ccaea3e80 100644 --- a/tests/exprs/tifexpr_typeinference.nim +++ b/tests/exprs/tifexpr_typeinference.nim @@ -1,11 +1,15 @@ +discard """ +action: compile +""" + #bug #712 import tables -proc test(): TTable[string, string] = +proc test(): Table[string, string] = discard -proc test2(): TTable[string, string] = +proc test2(): Table[string, string] = discard var x = 5 diff --git a/tests/flags/tgenscript.nim b/tests/flags/tgenscript.nim index 6a037b5d8..989ca8bcb 100644 --- a/tests/flags/tgenscript.nim +++ b/tests/flags/tgenscript.nim @@ -1,5 +1,7 @@ discard """ file: "tgenscript.nim" + target: "c" + action: compile """ echo "--genscript" diff --git a/tests/float/tfloatmod.nim b/tests/float/tfloatmod.nim new file mode 100644 index 000000000..0b84ec7e0 --- /dev/null +++ b/tests/float/tfloatmod.nim @@ -0,0 +1,130 @@ +discard """ + targets: "c c++ js" + output: "ok" + exitcode: "0" +""" + +# Test `mod` on float64 both at compiletime and at runtime +import math + +# Testdata from golang +const testValues: array[10, tuple[f64, expected: float64]] = [ + (4.9790119248836735e+00, 4.197615023265299782906368e-02), + (7.7388724745781045e+00, 2.261127525421895434476482e+00), + (-2.7688005719200159e-01, 3.231794108794261433104108e-02), + (-5.0106036182710749e+00, 4.989396381728925078391512e+00), + (9.6362937071984173e+00, 3.637062928015826201999516e-01), + (2.9263772392439646e+00, 1.220868282268106064236690e+00), + (5.2290834314593066e+00, 4.770916568540693347699744e+00), + (2.7279399104360102e+00, 1.816180268691969246219742e+00), + (1.8253080916808550e+00, 8.734595415957246977711748e-01), + (-8.6859247685756013e+00, 1.314075231424398637614104e+00)] + +const simpleTestData = [ + (5.0, 3.0, 2.0), + (5.0, -3.0, 2.0), + (-5.0, 3.0, -2.0), + (-5.0, -3.0, -2.0), + (10.0, 1.0, 0.0), + (10.0, 0.5, 0.0), + (10.0, 1.5, 1.0), + (-10.0, 1.0, -0.0), + (-10.0, 0.5, -0.0), + (-10.0, 1.5, -1.0), + (1.5, 1.0, 0.5), + (1.25, 1.0, 0.25), + (1.125, 1.0, 0.125) + ] + +const specialCases = [ + (-Inf, -Inf, Nan), + (-Inf, -Pi, Nan), + (-Inf, 0.0, Nan), + (-Inf, Pi, Nan), + (-Inf, Inf, Nan), + (-Inf, Nan, Nan), + (-PI, -Inf, -PI), + (-PI, 0.0, Nan), + (-PI, Inf, -PI), + (-PI, Nan, Nan), + (-0.0, -Inf, -0.0), + (-0.0, 0.0, Nan), + (-0.0, Inf, -0.0), + (-0.0, Nan, Nan), + (0.0, -Inf, 0.0), + (0.0, 0.0, Nan), + (0.0, Inf, 0.0), + (0.0, Nan, Nan), + (PI, -Inf, PI), + (PI, 0.0, Nan), + (PI, Inf, PI), + (PI, Nan, Nan), + (Inf, -Inf, Nan), + (Inf, -PI, Nan), + (Inf, 0.0, Nan), + (Inf, PI, Nan), + (Inf, Inf, Nan), + (Inf, Nan, Nan), + (Nan, -Inf, Nan), + (Nan, -PI, Nan), + (Nan, 0.0, Nan), + (Nan, PI, Nan), + (Nan, Inf, Nan), + (Nan, Nan, Nan)] + +const extremeValues = [ + (5.9790119248836734e+200, 1.1258465975523544, 0.6447968302508578), + (1.0e-100, 1.0e100, 1.0e-100)] + +proc errmsg(x, y, r, expected: float64): string = + $x & " mod " & $y & " == " & $r & " but expected " & $expected + +proc golangtest() = + let x = 10.0 + for tpl in testValues: + let (y, expected) = tpl + let r = x mod y + doAssert(r == expected, errmsg(x, y, r, expected)) + +proc simpletest() = + for tpl in simpleTestData: + let(x, y, expected) = tpl + let r = x mod y + doAssert(r == expected, errmsg(x, y, r, expected)) + +proc testSpecialCases() = + proc isnan(f: float64): bool = + case classify(f) + of fcNan: + result = true + else: + result = false + + for tpl in specialCases: + let(x, y, expected) = tpl + let r = x mod y + doAssert((r == expected) or (r.isnan and expected.isnan), + errmsg(x, y, r, expected)) + +proc testExtremeValues() = + for tpl in extremeValues: + let (x, y, expected) = tpl + let r = x mod y + doAssert(r == expected, errmsg(x, y, r, expected)) + +static: + # compiletime evaluation + golangtest() + simpletest() + testSpecialCases() + testExtremeValues() + +proc main() = + # runtime evaluation + golangtest() + simpletest() + testSpecialCases() + testExtremeValues() + +main() +echo "ok" diff --git a/tests/generics/t2tables.nim b/tests/generics/t2tables.nim index 3ef5e621e..e4b1fb967 100644 --- a/tests/generics/t2tables.nim +++ b/tests/generics/t2tables.nim @@ -1,3 +1,6 @@ +discard """ +action: compile +""" # bug #3669 @@ -10,4 +13,3 @@ type var g: G[string] echo g.rnodes["foo"] - diff --git a/tests/generics/tgeneric3.nim b/tests/generics/tgeneric3.nim index 6897d9de2..34b415446 100644 --- a/tests/generics/tgeneric3.nim +++ b/tests/generics/tgeneric3.nim @@ -1,3 +1,13 @@ +discard """ +output: ''' +312 +1000000 +1000000 +500000 +0 +''' +""" + import strutils type diff --git a/tests/generics/tgenerictmpl2.nim b/tests/generics/tgenerictmpl2.nim index ac92d3281..2efb000b3 100644 --- a/tests/generics/tgenerictmpl2.nim +++ b/tests/generics/tgenerictmpl2.nim @@ -21,7 +21,7 @@ ttmpl(1) ttmpl[int](1) #<- crash case #1 tproc[int]() -discard tproc[int] +let _ = tproc[int] ttmpl[int]() #<- crash case #2 ttmpl[int] #<- crash case #3 diff --git a/tests/generics/tgenericvariant.nim b/tests/generics/tgenericvariant.nim index 348d3da6e..73c8af825 100644 --- a/tests/generics/tgenericvariant.nim +++ b/tests/generics/tgenericvariant.nim @@ -1,3 +1,13 @@ +discard """ +output: ''' +Test +abcxyz123 +''' +""" + +proc fakeReadLine(): string = + "abcxyz123" + type TMaybe[T] = object case empty: bool @@ -12,7 +22,7 @@ proc Nothing[T](): TMaybe[T] = result.empty = true proc safeReadLine(): TMaybe[string] = - var r = stdin.readLine() + var r = fakeReadLine() if r == "": return Nothing[string]() else: return Just(r) @@ -21,3 +31,4 @@ when isMainModule: echo(Test.value) var mSomething = safeReadLine() echo(mSomething.value) + mSomething = safeReadLine() diff --git a/tests/generics/tlateboundstatic.nim b/tests/generics/tlateboundstatic.nim index f68f95f8d..90b44aa8e 100644 --- a/tests/generics/tlateboundstatic.nim +++ b/tests/generics/tlateboundstatic.nim @@ -1,5 +1,5 @@ discard """ - msg: "array[0..3, int]" + nimout: "array[0..3, int]" """ type diff --git a/tests/generics/trtree.nim b/tests/generics/trtree.nim index 75de2a1c4..321b31df6 100644 --- a/tests/generics/trtree.nim +++ b/tests/generics/trtree.nim @@ -1,6 +1,7 @@ discard """ output: '''1 [2, 3, 4, 7] [0, 0]''' + target: "c" """ # Nim RTree and R*Tree implementation @@ -81,13 +82,13 @@ proc distance(c1, c2: BoxCenter): auto = proc overlap(r1, r2: Box): auto = result = type(r1[0].a)(1) for i in 0 .. r1.high: - result *= (min(r1[i]. b, r2[i]. b) - max(r1[i]. a, r2[i]. a)) + result *= (min(r1[i].b, r2[i].b) - max(r1[i].a, r2[i].a)) if result <= 0: return 0 proc union(r1, r2: Box): Box = for i in 0 .. r1.high: - result[i]. a = min(r1[i]. a, r2[i]. a) - result[i]. b = max(r1[i]. b, r2[i]. b) + result[i].a = min(r1[i].a, r2[i].a) + result[i].b = max(r1[i].b, r2[i].b) proc intersect(r1, r2: Box): bool = for i in 0 .. r1.high: @@ -98,12 +99,12 @@ proc intersect(r1, r2: Box): bool = proc area(r: Box): auto = #type(r[0].a) = result = type(r[0].a)(1) for i in 0 .. r.high: - result *= r[i]. b - r[i]. a + result *= r[i].b - r[i].a proc margin(r: Box): auto = #type(r[0].a) = result = type(r[0].a)(0) for i in 0 .. r.high: - result += r[i]. b - r[i]. a + result += r[i].b - r[i].a # how much enlargement does r1 need to include r2 proc enlargement(r1, r2: Box): auto = @@ -238,12 +239,12 @@ proc rstarSplit[M, D: Dim; RT, LT](t: RStarTree[M, D, RT, LT]; n: var Node[M, D, for d2 in 0 ..< 2 * D: let d = d2 div 2 if d2 mod 2 == 0: - sortPlus(n.a, lx, proc (x, y: NL): int = cmp(x.b[d].a, y.b[d].a)) + sortPlus(n.a, lx, proc (x, y: NL): int = cmp(x.b[d].a, y.b[d].a)) else: - sortPlus(n.a, lx, proc (x, y: NL): int = cmp(x.b[d].b, y.b[d].b)) + sortPlus(n.a, lx, proc (x, y: NL): int = cmp(x.b[d].b, y.b[d].b)) for i in t.m - 1 .. n.a.high - t.m + 1: var b = lx.b - for j in 0 ..< i: # we can precalculate union() for range 0 .. t.m - 1, but that seems to give no real benefit. Maybe for very large M? + for j in 0 ..< i: # we can precalculate union() for range 0 .. t.m - 1, but that seems to give no real benefit.Maybe for very large M? #echo "x",j b = union(n.a[j].b, b) var m = margin(b) @@ -446,7 +447,7 @@ proc reInsert[M, D: Dim; RT, LT](t: RStarTree[M, D, RT, LT]; n: var Node[M, D, R while p.a[i].n != n: inc(i) let c = center(p.a[i].b) - sortPlus(n.a, lx, proc (x, y: NL): int = cmp(distance(center(x.b), c), distance(center(y.b), c))) + sortPlus(n.a, lx, proc (x, y: NL): int = cmp(distance(center(x.b), c), distance(center(y.b), c))) n.numEntries = M - t.p swap(n.a[n.numEntries], lx) inc n.numEntries diff --git a/tests/generics/tthread_generic.nim b/tests/generics/tthread_generic.nim index def1acfe1..f2e9cafa9 100644 --- a/tests/generics/tthread_generic.nim +++ b/tests/generics/tthread_generic.nim @@ -1,5 +1,6 @@ discard """ cmd: "nim $target --hints:on --threads:on $options $file" + action: compile """ type @@ -36,4 +37,3 @@ when isMainModule: echo("test") joinThread(thr) os.sleep(3000) - diff --git a/tests/global/tglobalforvar.nim b/tests/global/tglobalforvar.nim index af75df5c8..bc18f33f2 100644 --- a/tests/global/tglobalforvar.nim +++ b/tests/global/tglobalforvar.nim @@ -1,7 +1,9 @@ +discard """ +output: 100 +""" var funcs: seq[proc (): int {.nimcall.}] = @[] for i in 0..10: funcs.add((proc (): int = return i * i)) echo(funcs[3]()) - diff --git a/tests/init/t8314.nim b/tests/init/t8314.nim index 59d46eb33..47c8480c2 100644 --- a/tests/init/t8314.nim +++ b/tests/init/t8314.nim @@ -1,8 +1,14 @@ discard """ nimout: ''' -t8314.nim(8, 7) Hint: BEGIN [User] -t8314.nim(19, 7) Hint: END [User] +t8314.nim(14, 7) Hint: BEGIN [User] +t8314.nim(25, 7) Hint: END [User] ''' + +output: ''' +1 +1 +1 +''' """ {.hint: "BEGIN".} diff --git a/tests/init/tuninit1.nim b/tests/init/tuninit1.nim index 891f1e96c..67c0c4d8b 100644 --- a/tests/init/tuninit1.nim +++ b/tests/init/tuninit1.nim @@ -1,6 +1,7 @@ discard """ - msg: "Warning: 'y' might not have been initialized [Uninit]" + nimout: "Warning: 'y' might not have been initialized [Uninit]" line:34 + action: compile """ import strutils diff --git a/tests/iter/timplicit_auto.nim b/tests/iter/timplicit_auto.nim index d5cb95eb8..1b9f06843 100644 --- a/tests/iter/timplicit_auto.nim +++ b/tests/iter/timplicit_auto.nim @@ -15,4 +15,4 @@ iterator fields(a = (0,0), b = (h-1,w-1)): auto = yield (y,x) for y,x in fields(): - stdout.write disp[univ(x, y)] + doAssert disp[univ(x, y)] == disp[Tree] diff --git a/tests/iter/titer.nim b/tests/iter/titer.nim index c4143ae4f..22be1bad5 100644 --- a/tests/iter/titer.nim +++ b/tests/iter/titer.nim @@ -1,3 +1,16 @@ +discard """ +output: ''' +testtesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttest2!test3?hi +what's +your +name +hi +what's +your +name +''' +""" + # Test the new iterators iterator xrange(fromm, to: int, step = 1): int = diff --git a/tests/iter/titer_no_tuple_unpack.nim b/tests/iter/titer_no_tuple_unpack.nim index 13ec11bd6..d8df10189 100644 --- a/tests/iter/titer_no_tuple_unpack.nim +++ b/tests/iter/titer_no_tuple_unpack.nim @@ -1,3 +1,18 @@ +discard """ +output: ''' +3 4 +4 5 +5 6 +6 7 +7 8 +(x: 3, y: 4) +(x: 4, y: 5) +(x: 5, y: 6) +(x: 6, y: 7) +(x: 7, y: 8) +''' +""" + iterator xrange(fromm, to: int, step = 1): tuple[x, y: int] = var a = fromm @@ -10,4 +25,3 @@ for a, b in xrange(3, 7): for tup in xrange(3, 7): echo tup - diff --git a/tests/iter/titervaropenarray.nim b/tests/iter/titervaropenarray.nim index 1e70ce247..9eea085e3 100644 --- a/tests/iter/titervaropenarray.nim +++ b/tests/iter/titervaropenarray.nim @@ -11,6 +11,3 @@ iterator iterAndZero(a: var openArray[int]): int = var x = [[1, 2, 3], [4, 5, 6]] for y in iterAndZero(x[0]): write(stdout, $y) #OUT 123 - - - diff --git a/tests/iter/tpermutations.nim b/tests/iter/tpermutations.nim index 5149eb9c2..c5067ba31 100644 --- a/tests/iter/tpermutations.nim +++ b/tests/iter/tpermutations.nim @@ -1,3 +1,14 @@ +discard """ +output: ''' +@[@[1.0, 2.0], @[3.0, 4.0]] +perm: 10.0 det: -2.0 +@[@[1.0, 2.0, 3.0, 4.0], @[4.0, 5.0, 6.0, 7.0], @[7.0, 8.0, 9.0, 10.0], @[10.0, 11.0, 12.0, 13.0]] +perm: 29556.0 det: 0.0 +@[@[0.0, 1.0, 2.0, 3.0, 4.0], @[5.0, 6.0, 7.0, 8.0, 9.0], @[10.0, 11.0, 12.0, 13.0, 14.0], @[15.0, 16.0, 17.0, 18.0, 19.0], @[20.0, 21.0, 22.0, 23.0, 24.0]] +perm: 6778800.0 det: 0.0 +''' +""" + import sequtils, sugar diff --git a/tests/iter/tshallowcopy_closures.nim b/tests/iter/tshallowcopy_closures.nim index 2f024ee7e..279e7d950 100644 --- a/tests/iter/tshallowcopy_closures.nim +++ b/tests/iter/tshallowcopy_closures.nim @@ -1,5 +1,9 @@ discard """ ccodecheck: "!@('{' \\s* 'NI HEX3Astate;' \\s* '}')" + output: ''' +a1 10 +a1 9 +''' """ # bug #1803 @@ -26,6 +30,6 @@ var z: TaskFn discard x() -z = x #shallowCopy(z, x) -z = y #shallowCopy(z, y) +shallowCopy(z, x) +shallowCopy(z, y) discard x() diff --git a/tests/iter/twrap_walkdir.nim b/tests/iter/twrap_walkdir.nim index 4ac487d8e..1d52e9791 100644 --- a/tests/iter/twrap_walkdir.nim +++ b/tests/iter/twrap_walkdir.nim @@ -1,5 +1,6 @@ - - +discard """ +action: compile +""" import os diff --git a/tests/iter/tyieldintry.nim b/tests/iter/tyieldintry.nim index 6d24417a6..ee2790e54 100644 --- a/tests/iter/tyieldintry.nim +++ b/tests/iter/tyieldintry.nim @@ -440,4 +440,19 @@ block: test(it, 1, 2, 5) +block: #9694 - yield in ObjConstr + type Foo = object + a, b: int + + template yieldAndNum: int = + yield 1 + 2 + + iterator it(): int {.closure.} = + let a = Foo(a: 5, b: yieldAndNum()) + checkpoint(a.b) + + test(it, 1, 2) + echo "ok" + diff --git a/tests/js/tjsffi.nim b/tests/js/tjsffi.nim index 213d05964..2420c60f6 100644 --- a/tests/js/tjsffi.nim +++ b/tests/js/tjsffi.nim @@ -154,7 +154,7 @@ block: # Test JsAssoc .= and . block: proc test(): bool = - let obj = newJsAssoc[string, int]() + let obj = newJsAssoc[cstring, int]() var working = true obj.a = 11 obj.`$!&` = 42 @@ -168,7 +168,7 @@ block: # Test JsAssoc .() block: proc test(): bool = - let obj = newJsAssoc[string, proc(e: int): int]() + let obj = newJsAssoc[cstring, proc(e: int): int]() obj.a = proc(e: int): int = e * e obj.a(10) == 100 echo test() @@ -176,7 +176,7 @@ block: # Test JsAssoc []() block: proc test(): bool = - let obj = newJsAssoc[string, proc(e: int): int]() + let obj = newJsAssoc[cstring, proc(e: int): int]() obj.a = proc(e: int): int = e * e let call = obj["a"] call(10) == 100 @@ -185,7 +185,7 @@ block: # Test JsAssoc Iterators block: proc testPairs(): bool = - let obj = newJsAssoc[string, int]() + let obj = newJsAssoc[cstring, int]() var working = true obj.a = 10 obj.b = 20 @@ -202,7 +202,7 @@ block: return false working proc testItems(): bool = - let obj = newJsAssoc[string, int]() + let obj = newJsAssoc[cstring, int]() var working = true obj.a = 10 obj.b = 20 @@ -211,13 +211,13 @@ block: working = working and v in [10, 20, 30] working proc testKeys(): bool = - let obj = newJsAssoc[string, int]() + let obj = newJsAssoc[cstring, int]() var working = true obj.a = 10 obj.b = 20 obj.c = 30 for v in obj.keys: - working = working and v in ["a", "b", "c"] + working = working and v in [cstring"a", cstring"b", cstring"c"] working proc test(): bool = testPairs() and testItems() and testKeys() echo test() @@ -226,8 +226,8 @@ block: block: proc test(): bool = {. emit: "var comparison = {a: 22, b: 55};" .} - var comparison {. importcpp, nodecl .}: JsAssoc[string, int] - let obj = newJsAssoc[string, int]() + var comparison {. importcpp, nodecl .}: JsAssoc[cstring, int] + let obj = newJsAssoc[cstring, int]() obj.a = 22 obj.b = 55 obj.a == comparison.a and obj.b == comparison.b @@ -237,15 +237,15 @@ block: block: proc test(): bool = {. emit: "var comparison = {a: 22, b: 55};" .} - var comparison {. importcpp, nodecl .}: JsAssoc[string, int] - let obj = JsAssoc[string, int]{ a: 22, b: 55 } + var comparison {. importcpp, nodecl .}: JsAssoc[cstring, int] + let obj = JsAssoc[cstring, int]{ a: 22, b: 55 } var working = true working = working and compiles(JsAssoc[int, int]{ 1: 22, 2: 55 }) working = working and comparison.a == obj.a and comparison.b == obj.b working = working and - not compiles(JsAssoc[string, int]{ a: "test" }) + not compiles(JsAssoc[cstring, int]{ a: "test" }) working echo test() diff --git a/tests/lexer/tident.nim b/tests/lexer/tident.nim index 3327344a5..e5177436d 100644 --- a/tests/lexer/tident.nim +++ b/tests/lexer/tident.nim @@ -1,3 +1,16 @@ +discard """ +output: ''' +Length correct +Correct +Correct +Correct +Correct +Correct +Correct +Correct +Correct +''' +""" type TIdObj* = object of RootObj @@ -19,4 +32,3 @@ proc myNewString(L: int): string {.inline.} = echo("Wrong") var s = myNewString(8) - diff --git a/tests/lookups/test.nim b/tests/lookups/test.nim index a17d235a4..56f39a4d7 100644 --- a/tests/lookups/test.nim +++ b/tests/lookups/test.nim @@ -1,3 +1,10 @@ +discard """ +output: ''' +[Suite] memoization + +''' +""" + # This file needs to be called 'test' nim to provoke a clash # with the unittest.test name. Issue # @@ -14,4 +21,3 @@ proc fib(n: int): int = 40 suite "memoization": test "recursive function memoization": check fastFib(40) == fib(40) - diff --git a/tests/lookups/tprefer_proc.nim b/tests/lookups/tprefer_proc.nim deleted file mode 100644 index 57ee8e539..000000000 --- a/tests/lookups/tprefer_proc.nim +++ /dev/null @@ -1,4 +0,0 @@ - -# bug #4353 -import random -echo random[int](low(int) .. high(int)) diff --git a/tests/macros/tbindsym.nim b/tests/macros/tbindsym.nim index 2abcd98ce..a493d6a88 100644 --- a/tests/macros/tbindsym.nim +++ b/tests/macros/tbindsym.nim @@ -1,5 +1,5 @@ discard """ - msg: '''initApple + nimout: '''initApple deinitApple Coral enum diff --git a/tests/macros/tdumpastgen.nim b/tests/macros/tdumpastgen.nim index 0a1836886..0e0581f6a 100644 --- a/tests/macros/tdumpastgen.nim +++ b/tests/macros/tdumpastgen.nim @@ -1,5 +1,5 @@ discard """ -msg: '''nnkStmtList.newTree( +nimout: '''nnkStmtList.newTree( nnkVarSection.newTree( nnkIdentDefs.newTree( newIdentNode("x"), diff --git a/tests/macros/tdumptree.nim b/tests/macros/tdumptree.nim index 58b011b45..f540306c4 100644 --- a/tests/macros/tdumptree.nim +++ b/tests/macros/tdumptree.nim @@ -1,13 +1,14 @@ discard """ -msg: '''StmtList +nimout: ''' +StmtList VarSection IdentDefs - Ident !"x" + Ident "x" Empty Call DotExpr - Ident !"foo" - Ident !"create" + Ident "foo" + Ident "create" IntLit 56''' """ diff --git a/tests/macros/tgetimpl.nim b/tests/macros/tgetimpl.nim index 715c969f3..d231a4336 100644 --- a/tests/macros/tgetimpl.nim +++ b/tests/macros/tgetimpl.nim @@ -1,7 +1,7 @@ discard """ - msg: '''"muhaha" + nimout: '''"muhaha" proc poo(x, y: int) = - let y = x + let y = x echo ["poo"]''' """ @@ -16,14 +16,13 @@ proc poo(x, y: int) = macro m(x: typed): untyped = echo repr x.getImpl - result = x -discard m foo -discard m poo +m(foo) +m(poo) #------------ -macro checkOwner(x: typed, check_id: static[int]): untyped = +macro checkOwner(x: typed, check_id: static[int]): untyped = let sym = case check_id: of 0: x of 1: x.getImpl.body[0][0][0] @@ -32,11 +31,11 @@ macro checkOwner(x: typed, check_id: static[int]): untyped = else: x result = newStrLitNode($sym.owner.symKind) -macro isSameOwner(x, y: typed): untyped = - result = +macro isSameOwner(x, y: typed): untyped = + result = if x.owner == y.owner: bindSym"true" else: bindSym"false" - + static: doAssert checkOwner(foo, 0) == "nskModule" @@ -47,3 +46,22 @@ static: doAssert isSameOwner(foo, poo) doAssert isSameOwner(foo, echo) == false doAssert isSameOwner(poo, len) == false + +#--------------------------------------------------------------- + +macro check_gen_proc(ex: typed): (bool, bool) = + let lenChoice = bindsym"len" + var is_equal = false + var is_instance_of = false + for child in lenChoice: + if not is_equal: + is_equal = ex[0] == child + if not is_instance_of: + is_instance_of = isInstantiationOf(ex[0], child) + + result = nnkTupleConstr.newTree(newLit(is_equal), newLit(is_instance_of)) + +# check that len(seq[int]) is not equal to bindSym"len", but is instance of it +let a = @[1,2,3] +assert: check_gen_proc(len(a)) == (false, true) + diff --git a/tests/macros/tgettype.nim b/tests/macros/tgettype.nim index fa02bce57..77a55471f 100644 --- a/tests/macros/tgettype.nim +++ b/tests/macros/tgettype.nim @@ -1,6 +1,8 @@ discard """ -msg: '''ObjectTy(Sym(Model), RecList(Sym(name), Sym(password))) -BracketExpr(Sym(typeDesc), Sym(User))''' +output: ''' +(ObjectTy (Empty) (Sym "Model") (RecList (Sym "name") (Sym "password"))) +(BracketExpr (Sym "typeDesc") (Sym "User")) +''' """ import strutils, macros diff --git a/tests/macros/tgettype2.nim b/tests/macros/tgettype2.nim index f129e6e1b..c579cf6ff 100644 --- a/tests/macros/tgettype2.nim +++ b/tests/macros/tgettype2.nim @@ -1,3 +1,35 @@ +discard """ +output: ''' +############ +#### gt #### +############ +gt(Foo): typeDesc[Foo] +gt(Bar): typeDesc[Bar] +gt(Baz): typeDesc[int] +gt(foo): distinct[int] +gt(bar): distinct[int] +gt(baz): int, int +gt(v): seq[int] +gt(vv): seq[float] +gt(t): distinct[tuple[int, int]] +gt(tt): distinct[tuple[float, float]] +gt(s): distinct[tuple[int, int]] +############# +#### gt2 #### +############# +gt2(Foo): Foo +gt2(Bar): Bar +gt2(Baz): Baz +gt2(foo): Foo +gt2(bar): Bar +gt2(baz): Baz +gt2(v): seq[int] +gt2(vv): seq[float] +gt2(t): MyType[system.int] +gt2(tt): MyType[system.float] +gt2(s): MySimpleType +''' +""" import macros, typetraits diff --git a/tests/macros/tgettypeinst.nim b/tests/macros/tgettypeinst.nim index 6a99dc8b7..c2cde9a43 100644 --- a/tests/macros/tgettypeinst.nim +++ b/tests/macros/tgettypeinst.nim @@ -201,3 +201,14 @@ test(MyObj): _ = object {.packed,myAttr,serializationKey: "one".} myField: int myField2: float + +block t9600: + type + Apple = ref object of RootObj + + macro mixer(x: typed): untyped = + let w = getType(x) + let v = getTypeImpl(w[1]) + + var z: Apple + mixer(z) diff --git a/tests/macros/tmacrogenerics.nim b/tests/macros/tmacrogenerics.nim index 919a15b46..e4acdc321 100644 --- a/tests/macros/tmacrogenerics.nim +++ b/tests/macros/tmacrogenerics.nim @@ -1,8 +1,10 @@ discard """ file: "tmacrogenerics.nim" - msg: ''' -instantiation 1 with typedesc and typedesc -counter: 1 + nimout: ''' +instantiation 1 with None and None +instantiation 2 with None and None +instantiation 3 with None and None +counter: 3 ''' output: "int\nfloat\nint\nstring" """ diff --git a/tests/macros/tmacros_issues.nim b/tests/macros/tmacros_issues.nim index ecdcd5da9..657f30fc4 100644 --- a/tests/macros/tmacros_issues.nim +++ b/tests/macros/tmacros_issues.nim @@ -1,13 +1,10 @@ discard """ - msg: ''' -proc init(foo129050: int; bar129052: typedesc[int]): int = - foo129050 - + nimout: ''' IntLit 5 proc (x: int): string => typeDesc[proc[string, int]] proc (x: int): void => typeDesc[proc[void, int]] proc (x: int) => typeDesc[proc[void, int]] -x => uncheckedArray[int] +x => UncheckedArray[int] a s d @@ -43,8 +40,8 @@ block t7723: proc init(foo: int, bar: typedesc[int]): int = foo - expandMacros: - foo1() + #expandMacros: + foo1() doAssert init(1, int) == 1 @@ -144,11 +141,11 @@ block t1140: result.parse_template body[1].strVal - proc actual: string = tmpli html""" + proc actual: string {.used.} = tmpli html""" <p>Test!</p> """ - proc another: string = tmpli html""" + proc another: string {.used.} = tmpli html""" <p>what</p> """ diff --git a/tests/macros/tmacros_various.nim b/tests/macros/tmacros_various.nim index 15bd28a37..9eece00bd 100644 --- a/tests/macros/tmacros_various.nim +++ b/tests/macros/tmacros_various.nim @@ -1,9 +1,14 @@ discard """ - msg: ''' -range[0 .. 100] -array[0 .. 100, int] -10 -test + nimout: ''' +Infix + Ident "=>" + Call + Ident "name" + Ident "a" + ExprColonExpr + Ident "b" + Ident "cint" + NilLit ''' output: ''' diff --git a/tests/macros/tmemit.nim b/tests/macros/tmemit.nim index 3a3b8734b..06ab8a1e2 100644 --- a/tests/macros/tmemit.nim +++ b/tests/macros/tmemit.nim @@ -1,7 +1,9 @@ discard """ - output: '''HELLO WORLD + output: ''' +HELLO WORLD c_func -12''' +12 +''' """ import macros, strutils diff --git a/tests/macros/tnewlit.nim b/tests/macros/tnewlit.nim index 3ba1e09e1..194f035ba 100644 --- a/tests/macros/tnewlit.nim +++ b/tests/macros/tnewlit.nim @@ -147,3 +147,23 @@ block: # x needs to be of type seq[string] var x = test_newLit_empty_seq_string x.add("xyz") + +type + MyEnum = enum + meA + meB + +macro test_newLit_Enum: untyped = + result = newLit(meA) + +block: + let tmp: MyEnum = meA + doAssert tmp == test_newLit_Enum + +macro test_newLit_set: untyped = + let myset = {MyEnum.low .. MyEnum.high} + result = newLit(myset) + +block: + let tmp: set[MyEnum] = {MyEnum.low .. MyEnum.high} + doAssert tmp == test_newLit_set diff --git a/tests/macros/tquotedo.nim b/tests/macros/tquotedo.nim new file mode 100644 index 000000000..cd1f69116 --- /dev/null +++ b/tests/macros/tquotedo.nim @@ -0,0 +1,25 @@ +discard """ +output: ''' +123 +Hallo Welt +Hallo Welt +''' +""" + +import macros + +macro mac(): untyped = + quote do: + proc test(): int = + (proc(): int = result = 123)() + +mac() +echo test() + +macro foobar(arg: untyped): untyped = + result = arg + result.add quote do: + `result` + +foobar: + echo "Hallo Welt" diff --git a/tests/macros/tslice.nim b/tests/macros/tslice.nim new file mode 100644 index 000000000..c64289ec6 --- /dev/null +++ b/tests/macros/tslice.nim @@ -0,0 +1,38 @@ +import macros + +macro test(): untyped = + result = nnkStmtList.newTree() + let n = nnkStmtList.newTree( + newIdentNode("one"), + newIdentNode("two"), + newIdentNode("three"), + newIdentNode("four"), + newIdentNode("five"), + newIdentNode("six") + ) + + var i = 1 + for x in n[1 .. ^2]: + assert x == n[i] + i.inc + assert i == 5 + + i = 3 + for x in n[3..^1]: + assert x == n[i] + i.inc + assert i == 6 + + i = 0 + for x in n[0..3]: + assert x == n[i] + i.inc + assert i == 4 + + i = 0 + for x in n[0..5]: + assert x == n[i] + i.inc + assert i == 6 + +test() diff --git a/tests/macros/tstaticparamsmacro.nim b/tests/macros/tstaticparamsmacro.nim index ea59936e0..8bd653920 100644 --- a/tests/macros/tstaticparamsmacro.nim +++ b/tests/macros/tstaticparamsmacro.nim @@ -1,5 +1,6 @@ discard """ - msg: '''letters + nimout: ''' +letters aa bb numbers @@ -8,7 +9,7 @@ numbers AST a [(11, 22), (33, 44)] AST b -(e: [55, 66], f: [77, 88]) +([55, 66], [77, 88]) 55 10 20Test @@ -44,10 +45,10 @@ const b : Tb = (@[55,66], @[77, 88]) macro mA(data: static[Ta]): untyped = - echo "AST a \n", repr(data) + echo "AST a\n", repr(data) macro mB(data: static[Tb]): untyped = - echo "AST b \n", repr(data) + echo "AST b\n", repr(data) echo data.e[0] mA(a) diff --git a/tests/manyloc/keineschweine/dependencies/genpacket/genpacket_enet.nim b/tests/manyloc/keineschweine/dependencies/genpacket/genpacket_enet.nim index 3fb4dc7d9..dccbe61ba 100644 --- a/tests/manyloc/keineschweine/dependencies/genpacket/genpacket_enet.nim +++ b/tests/manyloc/keineschweine/dependencies/genpacket/genpacket_enet.nim @@ -2,7 +2,7 @@ import macros, macro_dsl, estreams from strutils import format template newLenName() = - let lenName {.inject.} = ^("len"& $lenNames) + let lenName {.inject.} = ^("len" & $lenNames) inc(lenNames) template defPacketImports*() {.dirty.} = @@ -18,7 +18,7 @@ macro defPacket*(typeNameN: untyped, typeFields: untyped): untyped = var constructorParams = newNimNode(nnkFormalParams).und(typeName) constructor = newNimNode(nnkProcDef).und( - postfix(^("new"& $typeName.ident), "*"), + postfix(^("new" & $typeName.ident), "*"), emptyNode(), emptyNode(), constructorParams, @@ -41,7 +41,7 @@ macro defPacket*(typeNameN: untyped, typeFields: untyped): untyped = emptyNode(), emptyNode()) read = newNimNode(nnkProcDef).und( - newIdentNode("read"& $typeName.ident).postfix("*"), + newIdentNode("read" & $typeName.ident).postfix("*"), emptyNode(), emptyNode(), newNimNode(nnkFormalParams).und( @@ -70,7 +70,7 @@ macro defPacket*(typeNameN: untyped, typeFields: untyped): untyped = let item = ^"item" ## item name in our iterators seqType = typeFields[i][1][1] ## type of seq - readName = newIdentNode("read"& $seqType.ident) + readName = newIdentNode("read" & $seqType.ident) readBody.add(newNimNode(nnkLetSection).und( newNimNode(nnkIdentDefs).und( lenName, @@ -100,7 +100,7 @@ macro defPacket*(typeNameN: untyped, typeFields: untyped): untyped = #set the default value to @[] (new sequence) typeFields[i][2] = "@".prefix(newNimNode(nnkBracket)) else: - error("Unknown type: "& treeRepr(typeFields[i])) + error("Unknown type: " & treeRepr(typeFields[i])) of nnkIdent: ##normal type case $typeFields[i][1].ident of "string": # length encoded string @@ -109,12 +109,12 @@ macro defPacket*(typeNameN: untyped, typeFields: untyped): untyped = of "int8", "int16", "int32", "float32", "float64", "char", "bool": packBody.add(newCall( "writeBE", streamID, dotName)) - readBody.add(resName := newCall("read"& $typeFields[i][1].ident, streamID)) + readBody.add(resName := newCall("read" & $typeFields[i][1].ident, streamID)) else: ## hopefully the type you specified was another defpacket() type packBody.add(newCall("pack", streamID, dotName)) - readBody.add(resName := newCall("read"& $typeFields[i][1].ident, streamID)) + readBody.add(resName := newCall("read" & $typeFields[i][1].ident, streamID)) else: - error("I dont know what to do with: "& treerepr(typeFields[i])) + error("I dont know what to do with: " & treerepr(typeFields[i])) var toStringFunc = newNimNode(nnkProcDef).und( @@ -137,7 +137,7 @@ macro defPacket*(typeNameN: untyped, typeFields: untyped): untyped = newNimNode(nnkCall).und(# [6][0][1] ^"format", ## format emptyNode())))) ## "[TypeName $1 $2]" - formatStr = "["& $typeName.ident + formatStr = "[" & $typeName.ident const emptyFields = {nnkEmpty, nnkNilLit} var objFields = newNimNode(nnkRecList) @@ -206,7 +206,7 @@ macro forwardPacket*(typeName: untyped, underlyingType: untyped): untyped = streamID = ^"s" result = newNimNode(nnkStmtList).und( newProc( - (^("read"& $typeName.ident)).postfix("*"), + (^("read" & $typeName.ident)).postfix("*"), [ iddefs("s", "PBuffer", newNimNode(nnkNilLit)) ], typeName), newProc( diff --git a/tests/manyloc/keineschweine/enet_server/enet_client.nim b/tests/manyloc/keineschweine/enet_server/enet_client.nim index ac600c0af..7b8576a14 100644 --- a/tests/manyloc/keineschweine/enet_server/enet_client.nim +++ b/tests/manyloc/keineschweine/enet_server/enet_client.nim @@ -217,7 +217,7 @@ proc lobbyUpdate*(dt: float) = gui.update(dt) i = (i + 1) mod 60 if i == 0: - fpsText.setString("FPS: "& ff(1.0/dt)) + fpsText.setString("FPS: " & ff(1.0/dt)) proc lobbyDraw*(window: PRenderWindow) = window.clear(Black) diff --git a/tests/manyloc/keineschweine/keineschweine.nim b/tests/manyloc/keineschweine/keineschweine.nim index 59347ee46..04fec2788 100644 --- a/tests/manyloc/keineschweine/keineschweine.nim +++ b/tests/manyloc/keineschweine/keineschweine.nim @@ -230,7 +230,7 @@ proc explode*(b: PLiveBullet) = proc bulletUpdate(body: PBody, gravity: TVector, damping, dt: CpFloat){.cdecl.} = body.UpdateVelocity(gravity, damping, dt) -template getPhysical() {.immediate.} = +template getPhysical() {.dirty.} = result.body = space.addBody(newBody( record.physics.mass, record.physics.moment)) @@ -281,7 +281,7 @@ proc draw*(window: PRenderWindow; b: PLiveBullet) {.inline.} = proc free*(veh: PVehicle) = - ("Destroying vehicle "& veh.record.name).echo + ("Destroying vehicle " & veh.record.name).echo destroy(veh.sprite) if veh.shape.isNil: "Free'd vehicle's shape was NIL!".echo else: space.removeShape(veh.shape) @@ -290,12 +290,12 @@ proc free*(veh: PVehicle) = veh.body.free() veh.shape.free() veh.sprite = nil - veh.body = nil + veh.body = nil veh.shape = nil proc newVehicle*(record: PVehicleRecord): PVehicle = - echo("Creating "& record.name) + echo("Creating " & record.name) new(result, free) result.record = record result.sprite = result.record.anim.spriteSheet.sprite.copy() diff --git a/tests/manyloc/keineschweine/lib/client_helpers.nim b/tests/manyloc/keineschweine/lib/client_helpers.nim index 5f819a7d1..0ebb4ade1 100644 --- a/tests/manyloc/keineschweine/lib/client_helpers.nim +++ b/tests/manyloc/keineschweine/lib/client_helpers.nim @@ -8,7 +8,7 @@ type addy: enet.TAddress host*: PHost peer*: PPeer - handlers*: TTable[char, TScPktHandler] + handlers*: Table[char, TScPktHandler] TScPktHandler* = proc(serv: PServer; buffer: PBuffer) TFileTransfer = object fileName: string diff --git a/tests/manyloc/keineschweine/lib/sg_assets.nim b/tests/manyloc/keineschweine/lib/sg_assets.nim index 90f0a54e9..7caa9c3fb 100644 --- a/tests/manyloc/keineschweine/lib/sg_assets.nim +++ b/tests/manyloc/keineschweine/lib/sg_assets.nim @@ -222,7 +222,7 @@ template cacheImpl(procName, cacheName, resultType, body: untyped) {.dirty.} = template checkFile(path: untyped) {.dirty.} = if not existsFile(path): - errors.add("File missing: "& path) + errors.add("File missing: " & path) cacheImpl newSprite, SpriteSheets, PSpriteSheet: result.file = filename @@ -231,7 +231,7 @@ cacheImpl newSprite, SpriteSheets, PSpriteSheet: result.frameh = strutils.parseInt(matches[1]) checkFile("data/gfx"/result.file) else: - errors.add "Bad file: "&filename&" must be in format name_WxH.png" + errors.add "Bad file: " & filename & " must be in format name_WxH.png" return cacheImpl newSound, SoundCache, PSoundRecord: @@ -314,10 +314,10 @@ proc validateSettings*(settings: JsonNode, errors: var seq[string]): bool = else: var id = 0 for i in items.items: - if i.kind != JArray: errors.add("Item #$1 is not an array"% $id) + if i.kind != JArray: errors.add("Item #$1 is not an array" % $id) elif i.len != 3: errors.add("($1) Item record should have 3 fields"%($id)) elif i[0].kind != JString or i[1].kind != JString or i[2].kind != JObject: - errors.add("($1) Item should be in form [name, type, {item: data}]"% $id) + errors.add("($1) Item should be in form [name, type, {item: data}]" % $id) result = false inc id @@ -332,10 +332,10 @@ proc loadSettings*(rawJson: string, errors: var seq[string]): bool = try: settings = parseJson(rawJson) except JsonParsingError: - errors.add("JSON parsing error: "& getCurrentExceptionMsg()) + errors.add("JSON parsing error: " & getCurrentExceptionMsg()) return except: - errors.add("Unknown exception: "& getCurrentExceptionMsg()) + errors.add("Unknown exception: " & getCurrentExceptionMsg()) return if not validateSettings(settings, errors): return @@ -378,7 +378,7 @@ proc loadSettings*(rawJson: string, errors: var seq[string]): bool = inc vID if itm.kind == Projectile: if itm.bullet.isNil: - errors.add("Projectile #$1 has no bullet!"% $vID) + errors.add("Projectile #$1 has no bullet!" % $vID) elif itm.bullet.id == -1: ## this item has an anonymous bullet, fix the ID and name itm.bullet.id = bID diff --git a/tests/manyloc/keineschweine/lib/sg_gui.nim b/tests/manyloc/keineschweine/lib/sg_gui.nim index 074e0604c..83ad0d1bf 100644 --- a/tests/manyloc/keineschweine/lib/sg_gui.nim +++ b/tests/manyloc/keineschweine/lib/sg_gui.nim @@ -260,7 +260,7 @@ proc update*(m: PMessageArea) = m.texts.add messageProto.copy() elif m.texts.len > m.sizeVisible: echo "cutting ", m.texts.len - m.sizeVisible, " fields" - for i in m.sizeVisible.. < m.texts.len: + for i in m.sizeVisible ..< m.texts.len: m.texts.pop().destroy() let nmsgs = m.messages.len() if m.sizeVisible == 0 or nmsgs == 0: diff --git a/tests/manyloc/keineschweine/lib/vehicles.nim b/tests/manyloc/keineschweine/lib/vehicles.nim index e245c9e8c..2c6e54d2b 100644 --- a/tests/manyloc/keineschweine/lib/vehicles.nim +++ b/tests/manyloc/keineschweine/lib/vehicles.nim @@ -23,7 +23,7 @@ proc strafe_left*(obj: PVehicle, dt: float) = VectorZero) proc strafe_right*(obj: PVehicle, dt: float) = obj.body.applyImpulse( - vectorForAngle(obj.body.getAngle()).rperp()* obj.record.handling.strafe * dt, + vectorForAngle(obj.body.getAngle()).rperp() * obj.record.handling.strafe * dt, VectorZero) proc turn_right*(obj: PVehicle, dt: float) = #obj.angle = (obj.angle + (obj.record.handling.rotation.float / 10.0 * dt)) mod TAU diff --git a/tests/metatype/tbindtypedesc.nim b/tests/metatype/tbindtypedesc.nim index 039acfbe9..cfa80e581 100644 --- a/tests/metatype/tbindtypedesc.nim +++ b/tests/metatype/tbindtypedesc.nim @@ -1,10 +1,5 @@ discard """ - msg: '''int int -float float -int int -TFoo TFoo -int float -TFoo TFoo''' + output: '''ok''' """ import typetraits @@ -86,3 +81,4 @@ reject bindArg(int, int, 10, 20, 30, "test") reject bindArg(int, string, 10.0, 20, "test", "nest") reject bindArg(int, string, "test", "nest", 10, 20) +echo "ok" diff --git a/tests/metatype/tmetatype_issues.nim b/tests/metatype/tmetatype_issues.nim index 5c5380c9f..c5040f9ba 100644 --- a/tests/metatype/tmetatype_issues.nim +++ b/tests/metatype/tmetatype_issues.nim @@ -1,4 +1,15 @@ - +discard """ +output:''' +void +(Field0: "string", Field1: "string") +1 mod 7 +@[2, 2, 2, 2, 2] +impl 2 called +asd +Foo +Bar +''' +""" import typetraits, macros @@ -145,6 +156,3 @@ block t3338: var t2 = Bar[int32]() t2.add() doAssert t2.x == 5 - - - diff --git a/tests/metatype/tsemistatic.nim b/tests/metatype/tsemistatic.nim index 3f36abde9..56b9a6218 100644 --- a/tests/metatype/tsemistatic.nim +++ b/tests/metatype/tsemistatic.nim @@ -1,5 +1,5 @@ discard """ - msg: "static 10\ndynamic\nstatic 20\n" + nimout: "static 10\ndynamic\nstatic 20\n" output: "s\nd\nd\ns" """ diff --git a/tests/metatype/tstaticparammacro.nim b/tests/metatype/tstaticparammacro.nim index 02021185f..16a6e56b8 100644 --- a/tests/metatype/tstaticparammacro.nim +++ b/tests/metatype/tstaticparammacro.nim @@ -1,5 +1,5 @@ discard """ - msg: '''letters + nimout: '''letters aa bb numbers @@ -8,7 +8,7 @@ numbers AST a [(11, 22), (33, 44)] AST b -(e: [55, 66], f: [77, 88]) +([55, 66], [77, 88]) 55 10 20Test @@ -44,10 +44,10 @@ const b : Tb = (@[55,66], @[77, 88]) macro mA(data: static[Ta]): untyped = - echo "AST a \n", repr(data) + echo "AST a\n", repr(data) macro mB(data: static[Tb]): untyped = - echo "AST b \n", repr(data) + echo "AST b\n", repr(data) echo data.e[0] mA(a) diff --git a/tests/metatype/ttypedesc3.nim b/tests/metatype/ttypedesc3.nim index 3d1cf2ec9..bd973eed1 100644 --- a/tests/metatype/ttypedesc3.nim +++ b/tests/metatype/ttypedesc3.nim @@ -1,3 +1,13 @@ +discard """ +output: ''' +proc Base +proc Child +method Base +yield Base +yield Child +''' +""" + import typetraits type diff --git a/tests/metatype/ttypetraits.nim b/tests/metatype/ttypetraits.nim index 106257828..2765a4231 100644 --- a/tests/metatype/ttypetraits.nim +++ b/tests/metatype/ttypetraits.nim @@ -1,5 +1,5 @@ discard """ - msg: "int\nstring\nTBar[int]" + nimout: "int\nstring\nTBar[int]" output: "int\nstring\nTBar[int]\nint\nrange 0..2(int)\nstring" disabled: true """ diff --git a/tests/misc/tcmdline.nim b/tests/misc/tcmdline.nim index cb8cb402c..2c4768716 100644 --- a/tests/misc/tcmdline.nim +++ b/tests/misc/tcmdline.nim @@ -1,3 +1,6 @@ +discard """ +outputsub: "Number of parameters: 0" +""" # Test the command line import diff --git a/tests/misc/tcolonisproc.nim b/tests/misc/tcolonisproc.nim index 665e9e604..c10dabcf1 100644 --- a/tests/misc/tcolonisproc.nim +++ b/tests/misc/tcolonisproc.nim @@ -1,3 +1,9 @@ +discard """ +output: ''' +1 +2 +''' +""" proc p(a, b: int, c: proc ()) = c() diff --git a/tests/misc/tdllvar.nim b/tests/misc/tdllvar.nim index 1c1238e8d..68029ddf4 100644 --- a/tests/misc/tdllvar.nim +++ b/tests/misc/tdllvar.nim @@ -1,3 +1,7 @@ +discard """ +disabled: true +""" + import os proc getDllName: string = @@ -12,5 +16,3 @@ proc myImport2(s: int) {.cdecl, importc, dynlib: getDllName().} myImport("test2") myImport2(12) - - diff --git a/tests/misc/tendian.nim b/tests/misc/tendian.nim deleted file mode 100644 index 91044f4d5..000000000 --- a/tests/misc/tendian.nim +++ /dev/null @@ -1,3 +0,0 @@ -# test the new endian magic - -writeLine(stdout, repr(system.cpuEndian)) diff --git a/tests/misc/tgetstartmilsecs.nim b/tests/misc/tgetstartmilsecs.nim deleted file mode 100644 index bf508dd54..000000000 --- a/tests/misc/tgetstartmilsecs.nim +++ /dev/null @@ -1,7 +0,0 @@ -# -import times, os - -var start = epochTime() -os.sleep(1000) - -echo epochTime() - start #OUT 1000 diff --git a/tests/misc/thallo.nim b/tests/misc/thallo.nim index 17e6089ed..6f9d49121 100644 --- a/tests/misc/thallo.nim +++ b/tests/misc/thallo.nim @@ -1,4 +1,8 @@ -# Hallo +discard """ +action: compile +""" + +# noted this seems to be an old test file designed for manual testing. import os, strutils, macros diff --git a/tests/misc/theaproots.nim b/tests/misc/theaproots.nim index 77d0207b0..1ea3c86b9 100644 --- a/tests/misc/theaproots.nim +++ b/tests/misc/theaproots.nim @@ -1,3 +1,7 @@ +discard """ +action: compile +""" + type Bar = object x: int diff --git a/tests/misc/tlastmod.nim b/tests/misc/tlastmod.nim index 538b5e656..1cc1d4bd9 100644 --- a/tests/misc/tlastmod.nim +++ b/tests/misc/tlastmod.nim @@ -1,18 +1,25 @@ +discard """ +outputsub: "is newer than" +""" # test the new LastModificationTime() proc +let + file1 = "tests/testdata/data.csv" + file2 = "tests/testdata/doc1.xml" + import os, times, strutils proc main() = var - a, b: TTime - a = getLastModificationTime(paramStr(1)) - b = getLastModificationTime(paramStr(2)) + a, b: Time + a = getLastModificationTime(file1) + b = getLastModificationTime(file2) writeLine(stdout, $a) writeLine(stdout, $b) if a < b: - write(stdout, "$2 is newer than $1\n" % [paramStr(1), paramStr(2)]) + write(stdout, "$2 is newer than $1\n" % [file1, file2]) else: - write(stdout, "$1 is newer than $2\n" % [paramStr(1), paramStr(2)]) + write(stdout, "$1 is newer than $2\n" % [file1, file2]) main() diff --git a/tests/misc/tloops.nim b/tests/misc/tloops.nim index b160500af..61e0baf10 100644 --- a/tests/misc/tloops.nim +++ b/tests/misc/tloops.nim @@ -1,3 +1,10 @@ +discard """ +output: ''' +Hello!(x: 1, y: 2, z: 3) +(x: 1.0, y: 2.0) +''' +""" + # Test nested loops and some other things proc andTest() = @@ -84,4 +91,3 @@ proc main[T]() = echo myType2 main[int]() - diff --git a/tests/misc/tmandelbrot.nim b/tests/misc/tmandelbrot.nim deleted file mode 100644 index 504628313..000000000 --- a/tests/misc/tmandelbrot.nim +++ /dev/null @@ -1,57 +0,0 @@ -discard """ - cmd: "nim $target --hints:on -d:release $options $file" -""" - -# -*- nim -*- - -import math -import os -import strutils - -type TComplex = tuple[re, im: float] - -proc `+` (a, b: TComplex): TComplex = - return (a.re + b.re, a.im + b.im) - -proc `*` (a, b: TComplex): TComplex = - result.re = a.re * b.re - a.im * b.im - result.im = a.re * b.im + a.im * b.re - -proc abs2 (a: TComplex): float = - return a.re * a.re + a.im * a.im - -var size = parseInt(paramStr(1)) -var bit = 128 -var byteAcc = 0 - -stdout.writeLine("P4") -stdout.write($size) -stdout.write(" ") -stdout.writeLine($size) - -var fsize = float(size) -for y in 0 .. size-1: - var fy = 2.0 * float(y) / fsize - 1.0 - for x in 0 .. size-1: - var z = (0.0, 0.0) - var c = (float(2*x) / fsize - 1.5, fy) - - block iter: - for i in 0 .. 49: - z = z*z + c - if abs2(z) >= 4.0: - break iter - byteAcc = byteAcc + bit - - if bit > 1: - bit = bit div 2 - else: - stdout.write(chr(byteAcc)) - bit = 128 - byteAcc = 0 - - if bit != 128: - stdout.write(chr(byteAcc)) - bit = 128 - byteAcc = 0 - diff --git a/tests/misc/tmemoization.nim b/tests/misc/tmemoization.nim index 840eb3b0d..c65692608 100644 --- a/tests/misc/tmemoization.nim +++ b/tests/misc/tmemoization.nim @@ -1,5 +1,5 @@ discard """ - msg: "test 1\ntest 2\ntest 3" + nimout: "test 1\ntest 2\ntest 3" output: "TEST 1\nTEST 2\nTEST 3" """ diff --git a/tests/misc/tnew.nim b/tests/misc/tnew.nim index 89f34a621..02282dd4a 100644 --- a/tests/misc/tnew.nim +++ b/tests/misc/tnew.nim @@ -1,3 +1,10 @@ +discard """ +outputsub: ''' +Simple tree node allocation worked! +Simple cycle allocation worked! +''' +""" + # Test the implementation of the new operator # and the code generation for gc walkers # (and the garbage collector): diff --git a/tests/misc/tnewuns.nim b/tests/misc/tnewuns.nim deleted file mode 100644 index d6bae4fb1..000000000 --- a/tests/misc/tnewuns.nim +++ /dev/null @@ -1,12 +0,0 @@ -# test the new unsigned operations: - -import - strutils - -var - x, y: int - -x = 1 -y = high(int) - -writeLine(stdout, $ ( x +% y ) ) diff --git a/tests/misc/tparseopt.nim b/tests/misc/tparseopt.nim index badbc59ad..651689398 100644 --- a/tests/misc/tparseopt.nim +++ b/tests/misc/tparseopt.nim @@ -9,6 +9,8 @@ kind: cmdLongOption key:val -- left: kind: cmdLongOption key:val -- debug:3 kind: cmdShortOption key:val -- l:4 kind: cmdShortOption key:val -- r:2 +cmdLongOption foo +cmdLongOption path parseoptNoVal kind: cmdLongOption key:val -- left: kind: cmdLongOption key:val -- debug:3 @@ -34,44 +36,49 @@ from parseopt2 import nil block: - echo "parseopt" - for kind, key, val in parseopt.getopt(): - echo "kind: ", kind, "\tkey:val -- ", key, ":", val + echo "parseopt" + for kind, key, val in parseopt.getopt(): + echo "kind: ", kind, "\tkey:val -- ", key, ":", val - # pass custom cmdline arguments - echo "first round" - var argv = "--left --debug:3 -l=4 -r:2" - var p = parseopt.initOptParser(argv) - for kind, key, val in parseopt.getopt(p): - echo "kind: ", kind, "\tkey:val -- ", key, ":", val - break - # reset getopt iterator and check arguments are returned correctly. - echo "second round" - for kind, key, val in parseopt.getopt(p): - echo "kind: ", kind, "\tkey:val -- ", key, ":", val + # pass custom cmdline arguments + echo "first round" + var argv = "--left --debug:3 -l=4 -r:2" + var p = parseopt.initOptParser(argv) + for kind, key, val in parseopt.getopt(p): + echo "kind: ", kind, "\tkey:val -- ", key, ":", val + break + # reset getopt iterator and check arguments are returned correctly. + echo "second round" + for kind, key, val in parseopt.getopt(p): + echo "kind: ", kind, "\tkey:val -- ", key, ":", val + + # bug #9619 + var x = parseopt.initOptParser(@["--foo:", "--path"], allowWhitespaceAfterColon = false) + for kind, key, val in parseopt.getopt(x): + echo kind, " ", key block: - echo "parseoptNoVal" - # test NoVal mode with custom cmdline arguments - var argv = "--left --debug:3 -l -r:2 --debug 2 --debug=1 -r1 -r=0 -lr4" - var p = parseopt.initOptParser(argv, - shortNoVal = {'l'}, longNoVal = @["left"]) - for kind, key, val in parseopt.getopt(p): - echo "kind: ", kind, "\tkey:val -- ", key, ":", val + echo "parseoptNoVal" + # test NoVal mode with custom cmdline arguments + var argv = "--left --debug:3 -l -r:2 --debug 2 --debug=1 -r1 -r=0 -lr4" + var p = parseopt.initOptParser(argv, + shortNoVal = {'l'}, longNoVal = @["left"]) + for kind, key, val in parseopt.getopt(p): + echo "kind: ", kind, "\tkey:val -- ", key, ":", val block: - echo "parseopt2" - for kind, key, val in parseopt2.getopt(): - echo "kind: ", kind, "\tkey:val -- ", key, ":", val + echo "parseopt2" + for kind, key, val in parseopt2.getopt(): + echo "kind: ", kind, "\tkey:val -- ", key, ":", val - # pass custom cmdline arguments - echo "first round" - var argv: seq[string] = @["--left", "--debug:3", "-l=4", "-r:2"] - var p = parseopt2.initOptParser(argv) - for kind, key, val in parseopt2.getopt(p): - echo "kind: ", kind, "\tkey:val -- ", key, ":", val - break - # reset getopt iterator and check arguments are returned correctly. - echo "second round" - for kind, key, val in parseopt2.getopt(p): - echo "kind: ", kind, "\tkey:val -- ", key, ":", val + # pass custom cmdline arguments + echo "first round" + var argv: seq[string] = @["--left", "--debug:3", "-l=4", "-r:2"] + var p = parseopt2.initOptParser(argv) + for kind, key, val in parseopt2.getopt(p): + echo "kind: ", kind, "\tkey:val -- ", key, ":", val + break + # reset getopt iterator and check arguments are returned correctly. + echo "second round" + for kind, key, val in parseopt2.getopt(p): + echo "kind: ", kind, "\tkey:val -- ", key, ":", val diff --git a/tests/misc/tprep.nim b/tests/misc/tprep.nim index 8f40300d6..45f25b790 100644 --- a/tests/misc/tprep.nim +++ b/tests/misc/tprep.nim @@ -1,3 +1,11 @@ +discard """ +nimout: ''' +tprep.nim(25, 9) Hint: Case 2 [User] +tprep.nim(27, 11) Hint: Case 2.3 [User] +''' +outputsub: "" +""" + # Test the features that used to belong to the preprocessor import diff --git a/tests/misc/tquicksort.nim b/tests/misc/tquicksort.nim index 0867a3769..017c73fbc 100644 --- a/tests/misc/tquicksort.nim +++ b/tests/misc/tquicksort.nim @@ -17,10 +17,7 @@ proc echoSeq(a: seq[int]) = for i in low(a)..high(a): echo(a[i]) -var - list: seq[int] - -list = QuickSort(@[89,23,15,23,56,123,356,12,7,1,6,2,9,4,3]) -echoSeq(list) - +let list = QuickSort(@[89,23,15,23,56,123,356,12,7,1,6,2,9,4,3]) +let expected = @[1, 2, 3, 4, 6, 7, 9, 12, 15, 23, 56, 89, 123, 356] +doAssert list == expected diff --git a/tests/misc/tradix.nim b/tests/misc/tradix.nim index 07674af18..5009dfcfb 100644 --- a/tests/misc/tradix.nim +++ b/tests/misc/tradix.nim @@ -1,3 +1,28 @@ +discard """ +output: ''' +false +false +false +false +false +false +false +false +false +false +128 +1 +2 +3 +4 +255 +17 +45 +19000 +4294967288 +''' +""" + # implements and tests an efficient radix tree ## another method to store an efficient array of pointers: diff --git a/tests/misc/treadln.nim b/tests/misc/treadln.nim index 6e01097aa..b716c4711 100644 --- a/tests/misc/treadln.nim +++ b/tests/misc/treadln.nim @@ -1,3 +1,11 @@ + +discard """ +output: ''' +test the improved readline handling that does not care whether its +Macintosh, Unix or Windows text format. +''' +""" + # test the improved readline handling that does not care whether its # Macintosh, Unix or Windows text format. @@ -8,5 +16,6 @@ var if open(inp, "tests/misc/treadln.nim"): while not endOfFile(inp): line = readLine(inp) - echo("#" & line & "#") + if line.len >= 2 and line[0] == '#' and line[1] == ' ': + echo line[2..^1] close(inp) diff --git a/tests/misc/treadx.nim b/tests/misc/treadx.nim deleted file mode 100644 index e68b8933d..000000000 --- a/tests/misc/treadx.nim +++ /dev/null @@ -1,13 +0,0 @@ - -when not defined(windows): - import posix - - var inp = "" - var buf: array[0..10, char] - while true: - var r = read(0, addr(buf), sizeof(buf)-1) - add inp, $cstring(addr buf) - if r != sizeof(buf)-1: break - - echo inp - #dafkladskölklödsaf ölksdakölfölksfklwe4iojr389wr 89uweokf sdlkf jweklr jweflksdj fioewjfsdlfsd diff --git a/tests/misc/treservedcidentsasfields.nim b/tests/misc/treservedcidentsasfields.nim new file mode 100644 index 000000000..a9a954651 --- /dev/null +++ b/tests/misc/treservedcidentsasfields.nim @@ -0,0 +1,39 @@ +discard """ + targets: "c cpp" +""" + +import macros + +macro make_test_type(idents: varargs[untyped]): untyped = + result = nnkStmtList.newTree() + + var ident_defs: seq[NimNode] = @[] + for i in idents: + ident_defs.add newIdentDefs(i, ident("int")) + + result.add newTree(nnkTypeSection, + newTree(nnkTypeDef, + ident("TestType"), + newEmptyNode(), + newTree(nnkObjectTy, + newEmptyNode(), + newEmptyNode(), + newTree(nnkRecList, + ident_defs + ) + ) + ) + ) + +make_test_type( + auto, bool, catch, char, class, compl, const_cast, default, delete, double, + dynamic_cast, explicit, extern, false, float, friend, goto, int, long, + mutable, namespace, new, operator, private, protected, public, register, + reinterpret_cast, restrict, short, signed, sizeof, static_cast, struct, switch, + this, throw, true, typedef, typeid, typeof, typename, union, packed, unsigned, + virtual, void, volatile, wchar_t, alignas, alignof, constexpr, decltype, nullptr, + noexcept, thread_local, static_assert, char16_t, char32_t +) + +# Make sure the type makes it to codegen. +var test_instance: TestType diff --git a/tests/misc/tshadow_magic_type.nim b/tests/misc/tshadow_magic_type.nim index 03c83079e..6f9716bb9 100644 --- a/tests/misc/tshadow_magic_type.nim +++ b/tests/misc/tshadow_magic_type.nim @@ -1,3 +1,10 @@ +discard """ +output: ''' +mylist +''' +""" + + type TListItemType* = enum RedisNil, RedisString @@ -15,7 +22,8 @@ proc seq*() = proc lrange*(key: string): TRedisList = var foo: TListItem - foo.kind = RedisNil + foo.kind = RedisString + foo.str = key result = @[foo] when isMainModule: diff --git a/tests/misc/tsizeof.nim b/tests/misc/tsizeof.nim index 2830a545f..f60c7fa00 100644 --- a/tests/misc/tsizeof.nim +++ b/tests/misc/tsizeof.nim @@ -10,14 +10,16 @@ type TMyArray2 = array[1..3, int32] TMyArray3 = array[TMyEnum, float64] +var failed = false + const mysize1 = sizeof(TMyArray1) mysize2 = sizeof(TMyArray2) mysize3 = sizeof(TMyArray3) -assert mysize1 == 3 -assert mysize2 == 12 -assert mysize3 == 32 +doAssert mysize1 == 3 +doAssert mysize2 == 12 +doAssert mysize3 == 32 import macros, typetraits @@ -38,6 +40,7 @@ macro testSizeAlignOf(args: varargs[untyped]): untyped = if nim_align != c_align: msg.add " align(get, expected): " & $nim_align & " != " & $c_align echo msg + failed = true macro testOffsetOf(a,b1,b2: untyped): untyped = @@ -48,7 +51,8 @@ macro testOffsetOf(a,b1,b2: untyped): untyped = c_offset = c_offsetof(`a`,`b1`) nim_offset = offsetof(`a`,`b2`) if c_offset != nim_offset: - echo `typeName`, ".", `member`, " offset: ", c_offset, " != ", nim_offset + echo `typeName`, ".", `member`, " offsetError, C: ", c_offset, " nim: ", nim_offset + failed = true template testOffsetOf(a,b: untyped): untyped = testOffsetOf(a,b,b) @@ -100,21 +104,16 @@ macro testAlign(arg:untyped):untyped = let nimAlign = alignof(`arg`) if cAlign != nimAlign: echo `prefix`, cAlign, " != ", nimAlign + failed = true -testAlign(pointer) -testAlign(int) -testAlign(uint) -testAlign(int8) -testAlign(int16) -testAlign(int32) -testAlign(int64) -testAlign(uint8) -testAlign(uint16) -testAlign(uint32) -testAlign(uint64) -testAlign(float) -testAlign(float32) -testAlign(float64) +macro testSize(arg:untyped):untyped = + let prefix = newLit(arg.lineinfo & " sizeof " & arg.repr & " ") + result = quote do: + let cSize = c_sizeof(`arg`) + let nimSize = sizeof(`arg`) + if cSize != nimSize: + echo `prefix`, cSize, " != ", nimSize + failed = true type MyEnum {.pure.} = enum @@ -142,14 +141,6 @@ type ValueA ValueB -testAlign(MyEnum) -testAlign(OtherEnum) -testAlign(Enum1) -testAlign(Enum2) -testAlign(Enum4) -testAlign(Enum8) - - template testinstance(body: untyped): untyped = block: {.pragma: objectconfig.} @@ -159,6 +150,32 @@ template testinstance(body: untyped): untyped = {.pragma: objectconfig, packed.} body + +proc testPrimitiveTypes(): void = + testAlign(pointer) + testAlign(int) + testAlign(uint) + testAlign(int8) + testAlign(int16) + testAlign(int32) + testAlign(int64) + testAlign(uint8) + testAlign(uint16) + testAlign(uint32) + testAlign(uint64) + testAlign(float) + testAlign(float32) + testAlign(float64) + + testAlign(MyEnum) + testAlign(OtherEnum) + testAlign(Enum1) + testAlign(Enum2) + testAlign(Enum4) + testAlign(Enum8) + +testPrimitiveTypes() + testinstance: type @@ -274,8 +291,6 @@ testinstance: const trivialSize = sizeof(TrivialType) # needs to be able to evaluate at compile time - testAlign(SimpleAlignment) - proc main(): void = var t : TrivialType var a : SimpleAlignment @@ -286,6 +301,7 @@ testinstance: var f : PaddingAfterBranch var g : RecursiveStuff var ro : RootObj + var e1: Enum1 e2: Enum2 @@ -295,8 +311,20 @@ testinstance: eoa: EnumObjectA eob: EnumObjectB + + testAlign(SimpleAlignment) + testSizeAlignOf(t,a,b,c,d,e,f,g,ro, e1, e2, e4, e8, eoa, eob) + when not defined(cpp): + type + WithBitsize {.objectconfig.} = object + bitfieldA {.bitsize: 16.}: uint32 + bitfieldB {.bitsize: 16.}: uint32 + + var wbs: WithBitsize + testSize(wbs) + testOffsetOf(TrivialType, x) testOffsetOf(TrivialType, y) testOffsetOf(TrivialType, z) @@ -304,6 +332,7 @@ testinstance: testOffsetOf(SimpleAlignment, a) testOffsetOf(SimpleAlignment, b) testOffsetOf(SimpleAlignment, c) + testOffsetOf(AlignAtEnd, a) testOffsetOf(AlignAtEnd, b) testOffsetOf(AlignAtEnd, c) @@ -322,11 +351,11 @@ testinstance: testOffsetOf(Foobar, c) - testOffsetOf(Bazing, a) - - testOffsetOf(InheritanceA, a) - testOffsetOf(InheritanceB, b) - testOffsetOf(InheritanceC, c) + when not defined(cpp): + testOffsetOf(Bazing, a) + testOffsetOf(InheritanceA, a) + testOffsetOf(InheritanceB, b) + testOffsetOf(InheritanceC, c) testOffsetOf(EnumObjectA, a) testOffsetOf(EnumObjectA, b) @@ -365,4 +394,7 @@ type assert sizeof(Bar) == 12 -echo "OK" +if failed: + quit("FAIL") +else: + echo "OK" diff --git a/tests/misc/tstrace.nim b/tests/misc/tstrace.nim index 23590d958..00af0af69 100644 --- a/tests/misc/tstrace.nim +++ b/tests/misc/tstrace.nim @@ -1,3 +1,23 @@ +discard """ +exitcode: 1 +output: ''' +Traceback (most recent call last) +tstrace.nim(36) tstrace +tstrace.nim(28) recTest +tstrace.nim(28) recTest +tstrace.nim(28) recTest +tstrace.nim(28) recTest +tstrace.nim(28) recTest +tstrace.nim(28) recTest +tstrace.nim(28) recTest +tstrace.nim(28) recTest +tstrace.nim(28) recTest +tstrace.nim(28) recTest +tstrace.nim(31) recTest +SIGSEGV: Illegal storage access. (Attempt to read from nil?) +''' +""" + # Test the new stacktraces (great for debugging!) {.push stack_trace: on.} diff --git a/tests/misc/tstrdist.nim b/tests/misc/tstrdist.nim index 3e1939e73..53ace2fae 100644 --- a/tests/misc/tstrdist.nim +++ b/tests/misc/tstrdist.nim @@ -23,4 +23,4 @@ proc editDistance(a, b: string): int = c[(i-1)*n + (j-1)] = min(x,min(y,z)) return c[n*m] -write(stdout, editDistance("abc", "abd")) +doAssert editDistance("abc", "abd") == 3 diff --git a/tests/misc/tunsigned64mod.nim b/tests/misc/tunsigned64mod.nim index 9c9e01c45..ca3286df3 100644 --- a/tests/misc/tunsigned64mod.nim +++ b/tests/misc/tunsigned64mod.nim @@ -12,13 +12,13 @@ let t4 = (v2 mod 2'u64).uint64 # works # bug #2550 var x: uint # doesn't work -echo x mod 2 == 0 +doAssert x mod 2 == 0 var y: uint64 # doesn't work -echo y mod 2 == 0 +doAssert y mod 2 == 0 var z: uint32 # works -echo z mod 2 == 0 +doAssert z mod 2 == 0 var a: int # works -echo a mod 2 == 0 +doAssert a mod 2 == 0 diff --git a/tests/misc/tvarious.nim b/tests/misc/tvarious.nim index 8124b3fc7..191107a87 100644 --- a/tests/misc/tvarious.nim +++ b/tests/misc/tvarious.nim @@ -1,3 +1,7 @@ +discard """ +action: compile +""" + # Test various aspects # bug #572 diff --git a/tests/modules/t8665.nim b/tests/modules/t8665.nim index 51538df79..74d31452f 100644 --- a/tests/modules/t8665.nim +++ b/tests/modules/t8665.nim @@ -1 +1,5 @@ +discard """ + action: compile +""" + import treorder diff --git a/tests/modules/texport2.nim b/tests/modules/texport2.nim index 6e55873c5..e90c58673 100644 --- a/tests/modules/texport2.nim +++ b/tests/modules/texport2.nim @@ -1,9 +1,16 @@ +discard """ +output: ''' +abc +xyz +B.foo +''' +""" + # bug #1595, #1612 import mexport2a proc main() = - echo "Import Test, two lines should follow. One with abc and one with xyz." printAbc() printXyz() diff --git a/tests/modules/timportas.nim b/tests/modules/timportas.nim index a92162117..2f7bf7f6a 100644 --- a/tests/modules/timportas.nim +++ b/tests/modules/timportas.nim @@ -10,7 +10,7 @@ import times as bar3 except convert import definitions as baz discard foo.v -discard bar.now -discard bar2.now -discard bar3.now -discard baz.v \ No newline at end of file +discard bar.now() +discard bar2.now() +discard bar3.now() +discard baz.v diff --git a/tests/newconfig/tfoo.nim b/tests/newconfig/tfoo.nim index 52ea841ee..f332cd6d4 100644 --- a/tests/newconfig/tfoo.nim +++ b/tests/newconfig/tfoo.nim @@ -1,7 +1,7 @@ discard """ cmd: "nim default $file" output: '''hello world! 0.5''' - msg: '''[NimScript] exec: gcc -v''' + nimout: '''[NimScript] exec: gcc -v''' """ when not defined(definedefine): diff --git a/tests/newconfig/tfoo.nims b/tests/newconfig/tfoo.nims index b9b9a87af..a9e40956e 100644 --- a/tests/newconfig/tfoo.nims +++ b/tests/newconfig/tfoo.nims @@ -3,9 +3,6 @@ mode = ScriptMode.Whatif exec "gcc -v" -# test that ospaths actually compiles: -import ospaths - --forceBuild --path: "../friends" diff --git a/tests/niminaction/Chapter3/ChatApp/src/client.nim b/tests/niminaction/Chapter3/ChatApp/src/client.nim index 4d139655c..d479ebf43 100644 --- a/tests/niminaction/Chapter3/ChatApp/src/client.nim +++ b/tests/niminaction/Chapter3/ChatApp/src/client.nim @@ -1,3 +1,7 @@ +discard """ +action: compile +""" + import os, threadpool, asyncdispatch, asyncnet import protocol diff --git a/tests/niminaction/Chapter3/ChatApp/src/server.nim b/tests/niminaction/Chapter3/ChatApp/src/server.nim index 8c572aeb0..31da74d16 100644 --- a/tests/niminaction/Chapter3/ChatApp/src/server.nim +++ b/tests/niminaction/Chapter3/ChatApp/src/server.nim @@ -1,3 +1,7 @@ +discard """ +action: compile +""" + import asyncdispatch, asyncnet type @@ -81,4 +85,4 @@ when isMainModule: echo("Server initialised!") # Execute the ``loop`` procedure. The ``waitFor`` procedure will run the # asyncdispatch event loop until the ``loop`` procedure finishes executing. - waitFor loop(server) \ No newline at end of file + waitFor loop(server) diff --git a/tests/niminaction/Chapter3/various3.nim b/tests/niminaction/Chapter3/various3.nim index 478229b00..7b2776d70 100644 --- a/tests/niminaction/Chapter3/various3.nim +++ b/tests/niminaction/Chapter3/various3.nim @@ -1,3 +1,9 @@ +discard """ +output: ''' +Future is no longer empty, 42 +''' +""" + import threadpool proc foo: string = "Dog" var x: FlowVar[string] = spawn foo() @@ -33,9 +39,9 @@ let data = """ {"username": "Dominik"} """ -let obj = parseJson(data) -assert obj.kind == JObject -assert obj["username"].kind == JString +let obj = parseJson(data) +assert obj.kind == JObject +assert obj["username"].kind == JString assert obj["username"].str == "Dominik" block: @@ -60,12 +66,12 @@ var amy = Human(name: "Amy", age: 20) import asyncdispatch -var future = newFuture[int]() -doAssert(not future.finished) +var future = newFuture[int]() +doAssert(not future.finished) -future.callback = - proc (future: Future[int]) = - echo("Future is no longer empty, ", future.read) +future.callback = + proc (future: Future[int]) = + echo("Future is no longer empty, ", future.read) future.complete(42) @@ -85,9 +91,8 @@ import asyncdispatch, asyncfile, os proc readFiles() {.async.} = # --- Changed to getTempDir here. var file = openAsync(getTempDir() / "test.txt", fmReadWrite) - let data = await file.readAll() - echo(data) - await file.write("Hello!\n") + let data = await file.readAll() + echo(data) + await file.write("Hello!\n") waitFor readFiles() - diff --git a/tests/niminaction/Chapter6/WikipediaStats/concurrency.nim b/tests/niminaction/Chapter6/WikipediaStats/concurrency.nim index 478f533d9..f20e21f4d 100644 --- a/tests/niminaction/Chapter6/WikipediaStats/concurrency.nim +++ b/tests/niminaction/Chapter6/WikipediaStats/concurrency.nim @@ -1,3 +1,7 @@ +discard """ +action: compile +""" + # See this page for info about the format https://wikitech.wikimedia.org/wiki/Analytics/Data/Pagecounts-all-sites import tables, parseutils, strutils, threadpool diff --git a/tests/niminaction/Chapter6/WikipediaStats/concurrency_regex.nim b/tests/niminaction/Chapter6/WikipediaStats/concurrency_regex.nim index 8df3b6aeb..dbd635634 100644 --- a/tests/niminaction/Chapter6/WikipediaStats/concurrency_regex.nim +++ b/tests/niminaction/Chapter6/WikipediaStats/concurrency_regex.nim @@ -1,3 +1,7 @@ +discard """ +action: compile +""" + # See this page for info about the format https://wikitech.wikimedia.org/wiki/Analytics/Data/Pagecounts-all-sites import tables, parseutils, strutils, threadpool, re diff --git a/tests/niminaction/Chapter6/WikipediaStats/naive.nim b/tests/niminaction/Chapter6/WikipediaStats/naive.nim index ed4fba8e2..ce995efaf 100644 --- a/tests/niminaction/Chapter6/WikipediaStats/naive.nim +++ b/tests/niminaction/Chapter6/WikipediaStats/naive.nim @@ -1,3 +1,7 @@ +discard """ +action: compile +""" + # See this page for info about the format https://wikitech.wikimedia.org/wiki/Analytics/Data/Pagecounts-all-sites import tables, parseutils, strutils diff --git a/tests/niminaction/Chapter6/WikipediaStats/parallel_counts.nim b/tests/niminaction/Chapter6/WikipediaStats/parallel_counts.nim index 7181145e9..74857367a 100644 --- a/tests/niminaction/Chapter6/WikipediaStats/parallel_counts.nim +++ b/tests/niminaction/Chapter6/WikipediaStats/parallel_counts.nim @@ -1,3 +1,7 @@ +discard """ +action: compile +""" + import os, parseutils, threadpool, strutils type @@ -69,4 +73,4 @@ proc readPageCounts(filename: string, chunkSize = 1_000_000) = when isMainModule: const file = "pagecounts-20160101-050000" let filename = getCurrentDir() / file - readPageCounts(filename) \ No newline at end of file + readPageCounts(filename) diff --git a/tests/niminaction/Chapter6/WikipediaStats/race_condition.nim b/tests/niminaction/Chapter6/WikipediaStats/race_condition.nim index c62b2f93e..db68aeb5c 100644 --- a/tests/niminaction/Chapter6/WikipediaStats/race_condition.nim +++ b/tests/niminaction/Chapter6/WikipediaStats/race_condition.nim @@ -1,3 +1,7 @@ +discard """ +action: compile +""" + import threadpool var counter = 0 @@ -10,4 +14,4 @@ proc increment(x: int) = spawn increment(10_000) spawn increment(10_000) sync() -echo(counter) \ No newline at end of file +echo(counter) diff --git a/tests/niminaction/Chapter6/WikipediaStats/sequential_counts.nim b/tests/niminaction/Chapter6/WikipediaStats/sequential_counts.nim index 25ad7d5f4..102dd15d3 100644 --- a/tests/niminaction/Chapter6/WikipediaStats/sequential_counts.nim +++ b/tests/niminaction/Chapter6/WikipediaStats/sequential_counts.nim @@ -1,3 +1,7 @@ +discard """ +action: compile +""" + import os, parseutils proc parse(line: string, domainCode, pageTitle: var string, @@ -31,4 +35,4 @@ proc readPageCounts(filename: string) = when isMainModule: const file = "pagecounts-20160101-050000" let filename = getCurrentDir() / file - readPageCounts(filename) \ No newline at end of file + readPageCounts(filename) diff --git a/tests/niminaction/Chapter7/Tweeter/src/createDatabase.nim b/tests/niminaction/Chapter7/Tweeter/src/createDatabase.nim index c7aee1b44..a7d4ebe00 100644 --- a/tests/niminaction/Chapter7/Tweeter/src/createDatabase.nim +++ b/tests/niminaction/Chapter7/Tweeter/src/createDatabase.nim @@ -1,6 +1,10 @@ +discard """ +output: "Database created successfully!" +""" + import database var db = newDatabase() db.setup() echo("Database created successfully!") -db.close() \ No newline at end of file +db.close() diff --git a/tests/niminaction/Chapter7/Tweeter/src/tweeter.nim b/tests/niminaction/Chapter7/Tweeter/src/tweeter.nim index b8a36306e..12aaf49b8 100644 --- a/tests/niminaction/Chapter7/Tweeter/src/tweeter.nim +++ b/tests/niminaction/Chapter7/Tweeter/src/tweeter.nim @@ -1,3 +1,7 @@ +discard """ +action: compile +""" + import asyncdispatch, times import jester diff --git a/tests/niminaction/Chapter7/Tweeter/tests/database_test.nim b/tests/niminaction/Chapter7/Tweeter/tests/database_test.nim index 926ca452c..da69a004c 100644 --- a/tests/niminaction/Chapter7/Tweeter/tests/database_test.nim +++ b/tests/niminaction/Chapter7/Tweeter/tests/database_test.nim @@ -1,3 +1,7 @@ +discard """ +outputsub: "All tests finished successfully!" +""" + import database, os, times when isMainModule: diff --git a/tests/niminaction/Chapter8/canvas/canvas.nim b/tests/niminaction/Chapter8/canvas/canvas.nim index 713d1e9e2..ae2765630 100644 --- a/tests/niminaction/Chapter8/canvas/canvas.nim +++ b/tests/niminaction/Chapter8/canvas/canvas.nim @@ -1,3 +1,7 @@ +discard """ +action: compile +""" + import dom type diff --git a/tests/niminaction/Chapter8/sdl/sdl_test.nim b/tests/niminaction/Chapter8/sdl/sdl_test.nim index a572d5231..a49e08911 100644 --- a/tests/niminaction/Chapter8/sdl/sdl_test.nim +++ b/tests/niminaction/Chapter8/sdl/sdl_test.nim @@ -1,3 +1,7 @@ +discard """ +action: compile +""" + import os import sdl diff --git a/tests/niminaction/Chapter8/sfml/sfml_test.nim b/tests/niminaction/Chapter8/sfml/sfml_test.nim index 7d56d0903..e71060cb4 100644 --- a/tests/niminaction/Chapter8/sfml/sfml_test.nim +++ b/tests/niminaction/Chapter8/sfml/sfml_test.nim @@ -1,5 +1,6 @@ discard """ - disabled: "windows" +action: compile +disabled: "windows" """ import sfml, os diff --git a/tests/objects/tobjcov.nim b/tests/objects/tobjcov.nim index c766adde0..817c1fcda 100644 --- a/tests/objects/tobjcov.nim +++ b/tests/objects/tobjcov.nim @@ -1,3 +1,7 @@ +discard """ +action: compile +""" + # Covariance is not type safe: type @@ -14,4 +18,3 @@ proc bp(x: var TB) = x.b[high(x.b)] = -1 var f = cast[proc (x: var TA) {.nimcall.}](bp) var a: TA f(a) # bp expects a TB, but gets a TA - diff --git a/tests/objects/tobject.nim b/tests/objects/tobject.nim index cdb8f80db..61ef7442e 100644 --- a/tests/objects/tobject.nim +++ b/tests/objects/tobject.nim @@ -1,3 +1,7 @@ +discard """ +output: "[Suite] object basic methods" +""" + import unittest type Obj = object diff --git a/tests/objects/tobjects.nim b/tests/objects/tobjects.nim deleted file mode 100644 index 66a38960e..000000000 --- a/tests/objects/tobjects.nim +++ /dev/null @@ -1,52 +0,0 @@ -type - TBase = object of RootObj - x, y: int - - TSubclassKind = enum ka, kb, kc, kd, ke, kf - TSubclass = object of TBase - case c: TSubclassKind - of ka, kb, kc, kd: - a, b: int - of ke: - d, e, f: char - else: nil - n: bool - -type - TMyObject = object of RootObj - case disp: range[0..4] - of 0: arg: char - of 1: s: string - else: wtf: bool - -var - x: TMyObject - -var - global: int - -var - s: string - r: float = 0.0 - i: int = 500 + 400 - -case i -of 500..999: write(stdout, "ha!\n") -of 1000..3000, 12: write(stdout, "ganz schön groß\n") -of 1, 2, 3: write(stdout, "1 2 oder 3\n") -else: write(stdout, "sollte nicht passieren\n") - -case readLine(stdin) -of "Rumpf": write(stdout, "Hallo Meister!\n") -of "Andreas": write(stdout, "Hallo Meister!\n") -else: write(stdout, "Nicht mein Meister!\n") - -global = global + 1 -write(stdout, "Hallo wie heißt du? \n") -s = readLine(stdin) -i = 0 -while i < len(s): - if s[i] == 'c': write(stdout, "'c' in deinem Namen gefunden\n") - i = i + 1 - -write(stdout, "Du heißt " & s) diff --git a/tests/objvariant/tcheckedfield1.nim b/tests/objvariant/tcheckedfield1.nim index a7f232c5b..69b099f24 100644 --- a/tests/objvariant/tcheckedfield1.nim +++ b/tests/objvariant/tcheckedfield1.nim @@ -1,6 +1,8 @@ discard """ - msg: "Warning: cannot prove that field 'x.s' is accessible [ProveField]" + nimout: "Warning: cannot prove that field 'x.s' is accessible [ProveField]" line:51 + action: run + output: "abc abc" """ import strutils diff --git a/tests/osproc/ta_in.nim b/tests/osproc/ta_in.nim index b46890f6e..fb294ec14 100644 --- a/tests/osproc/ta_in.nim +++ b/tests/osproc/ta_in.nim @@ -1,3 +1,7 @@ +discard """ +action: compile +""" + # This file is prefixed with an "a", because other tests # depend on it and it must be compiled first. import strutils diff --git a/tests/osproc/ta_out.nim b/tests/osproc/ta_out.nim index f7091a7f6..318a27d59 100644 --- a/tests/osproc/ta_out.nim +++ b/tests/osproc/ta_out.nim @@ -1,3 +1,14 @@ +discard """ +output: ''' +to stdout +to stdout +to stderr +to stderr +to stdout +to stdout +''' +""" + # This file is prefixed with an "a", because other tests # depend on it and it must be compiled first. stdout.writeLine("to stdout") diff --git a/tests/osproc/tafalse.nim b/tests/osproc/tafalse.nim index 24fd4fb2e..05a0bfce9 100644 --- a/tests/osproc/tafalse.nim +++ b/tests/osproc/tafalse.nim @@ -1,3 +1,7 @@ +discard """ +exitcode: 1 +""" + # 'tafalse.nim' to ensure it is compiled before texitcode.nim import system quit(QuitFailure) diff --git a/tests/osproc/texitcode.nim b/tests/osproc/texitcode.nim index 4eaab6da2..6dc5508b5 100644 --- a/tests/osproc/texitcode.nim +++ b/tests/osproc/texitcode.nim @@ -2,6 +2,7 @@ discard """ file: "texitcode.nim" output: "" """ + import osproc, os const filename = when defined(Windows): "tafalse.exe" else: "tafalse" diff --git a/tests/overload/tselfderef.nim b/tests/overload/tselfderef.nim index 708e4043b..96f1da42a 100644 --- a/tests/overload/tselfderef.nim +++ b/tests/overload/tselfderef.nim @@ -1,7 +1,10 @@ +discard """ +action: compile +""" + # bug #4671 {.experimental.} {.this: self.} - type SomeObj = object f: int diff --git a/tests/parallel/tarray_of_channels.nim b/tests/parallel/tarray_of_channels.nim index 90ae8369c..e2a682bd5 100644 --- a/tests/parallel/tarray_of_channels.nim +++ b/tests/parallel/tarray_of_channels.nim @@ -1,3 +1,15 @@ +discard """ +sortoutput: true +output: ''' +(x: 0.0) +(x: 0.0) +(x: 0.0) +test +test +test +''' +""" + # bug #2257 import threadpool diff --git a/tests/parallel/tdont_be_stupid.nim b/tests/parallel/tdont_be_stupid.nim index a7e82466a..d765c11a9 100644 --- a/tests/parallel/tdont_be_stupid.nim +++ b/tests/parallel/tdont_be_stupid.nim @@ -1,3 +1,11 @@ +discard """ +output: ''' +100 +200 +300 +400 +''' +""" import threadpool, os @@ -12,4 +20,4 @@ proc sleepsort(nums: openArray[int]) = spawn single(nums[i]) i += 1 -sleepsort([50,3,40,25]) +sleepsort([400,100,300,200]) diff --git a/tests/parallel/tguard1.nim b/tests/parallel/tguard1.nim index c7972d225..b1eb7e7c5 100644 --- a/tests/parallel/tguard1.nim +++ b/tests/parallel/tguard1.nim @@ -1,3 +1,7 @@ +discard """ +output: "90" +""" + when false: template lock(a, b: ptr Lock; body: stmt) = diff --git a/tests/parallel/tlet_spawn.nim b/tests/parallel/tlet_spawn.nim index 463ee1a47..62341d8f0 100644 --- a/tests/parallel/tlet_spawn.nim +++ b/tests/parallel/tlet_spawn.nim @@ -1,3 +1,8 @@ +discard """ +output: ''' +done999 999 +''' +""" import threadpool diff --git a/tests/parallel/tmissing_deepcopy.nim b/tests/parallel/tmissing_deepcopy.nim index 53481e4df..45fdf0f8f 100644 --- a/tests/parallel/tmissing_deepcopy.nim +++ b/tests/parallel/tmissing_deepcopy.nim @@ -1,5 +1,6 @@ discard """ - ccodeCheck: "\\i @'deepCopy(' .*" + ccodeCheck: "@'genericDeepCopy(' .*" + action: compile """ # bug #2286 diff --git a/tests/parallel/tsimple_array_checks.nim b/tests/parallel/tsimple_array_checks.nim index 9874d3299..ee9508074 100644 --- a/tests/parallel/tsimple_array_checks.nim +++ b/tests/parallel/tsimple_array_checks.nim @@ -1,3 +1,25 @@ +discard """ +sortoutput: true +output: ''' +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +Hello 1 +Hello 2 +Hello 3 +Hello 4 +Hello 5 +Hello 6 +''' +""" + # bug #2287 import threadPool diff --git a/tests/parser/tprecedence.nim b/tests/parser/tprecedence.nim index d586f14a3..aff7c6aca 100644 --- a/tests/parser/tprecedence.nim +++ b/tests/parser/tprecedence.nim @@ -1,7 +1,8 @@ discard """ output: '''holla true -defabc 4''' +defabc 4 +0''' """ # Test top level semicolon works properly: @@ -19,3 +20,23 @@ proc foo[S, T](x: S, y: T): T = x & y proc bar[T](x: T): T = x echo "def".foo[:string, string]("abc"), " ", 4.bar[:int] + +# bug #9574 +proc isFalse(a: int): bool = false + +assert not isFalse(3) + +# bug #9633 + +type + MyField = object + b: seq[string] + + MyObject = object + f: MyField + +proc getX(x: MyObject): lent MyField {.inline.} = + x.f + +let a = MyObject() +echo a.getX.b.len diff --git a/tests/parser/tstrongspaces.nim b/tests/parser/tstrongspaces.nim deleted file mode 100644 index adab7f709..000000000 --- a/tests/parser/tstrongspaces.nim +++ /dev/null @@ -1,83 +0,0 @@ -#? strongSpaces - -discard """ - output: '''35 -true -true -4 -true -1 -false -77 -(Field0: 1, Field1: 2, Field2: 2) -ha -true -tester args -all -all args -19 --3 -false --2 -''' -""" - -echo 2+5 * 5 - -# Keyword operators -echo 1 + 16 shl 1 == 1 + (16 shl 1) -echo 2 and 1 in {0, 30} -echo 2+2 * 2 shr 1 -echo false or 2 and 1 in {0, 30} - -proc `^`(a, b: int): int = a + b div 2 -echo 19 mod 16 ^ 4 + 2 and 1 -echo 18 mod 16 ^ 4 > 0 - -# echo $foo gotcha -let foo = 77 -echo $foo - -echo (1, 2, 2) - -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 - let c = 90 - let bar = 8000 - if foo+4 * 4 == 8 and b&c | 9 ++ - bar: - echo "ho" - else: - echo "ha" - - let booA = foo+4 * 4 - b&c | 9 + - bar - # is parsed as - let booB = ((foo+4)*4) - ((b&c) | 9) + bar - - echo booA == booB - - -template `|`(a, b): untyped = (if a.len > 0: a else: b) - -const - tester = "tester" - args = "args" - -echo tester & " " & args|"all" -echo "all" | tester & " " & args -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): int = a - b shr 1 - -echo 3 +-> 2 + 2 and 4 -var arrowed = 3+->2 + 2 and 4 # arrowed = 4 -echo arrowed ===> 15 -echo (2 * 3+->2) == (2*3 +-> 2) -echo arrowed ===> 2 + 3+->2 diff --git a/tests/pragmas/tnoreturn.nim b/tests/pragmas/tnoreturn.nim index 50b427d71..6d0466df3 100644 --- a/tests/pragmas/tnoreturn.nim +++ b/tests/pragmas/tnoreturn.nim @@ -1,5 +1,6 @@ discard """ ccodeCheck: "\\i @'__attribute__((noreturn))' .*" +action: compile """ proc noret1*(i: int) {.noreturn.} = diff --git a/tests/seq/tseq.nim b/tests/seq/tseq.nim index 6528d518e..1cb94b308 100644 --- a/tests/seq/tseq.nim +++ b/tests/seq/tseq.nim @@ -170,6 +170,30 @@ block tshallowseq: xxx() +block tshallowemptyseq: + proc test() = + var nilSeq: seq[int] = @[] + var emptySeq: seq[int] = newSeq[int]() + block: + var t = @[1,2,3] + shallow(nilSeq) + t = nilSeq + doAssert t == @[] + block: + var t = @[1,2,3] + shallow(emptySeq) + t = emptySeq + doAssert t == @[] + block: + var t = @[1,2,3] + shallowCopy(t, nilSeq) + doAssert t == @[] + block: + var t = @[1,2,3] + shallowCopy(t, emptySeq) + doAssert t == @[] + test() + import strutils block ttoseq: diff --git a/tests/sets/tsets_various.nim b/tests/sets/tsets_various.nim index 7cb9a6eec..8a63763b4 100644 --- a/tests/sets/tsets_various.nim +++ b/tests/sets/tsets_various.nim @@ -102,9 +102,9 @@ block tsets2: block tsets3: let - s1: TSet[int] = toSet([1, 2, 4, 8, 16]) - s2: TSet[int] = toSet([1, 2, 3, 5, 8]) - s3: TSet[int] = toSet([3, 5, 7]) + s1: HashSet[int] = toSet([1, 2, 4, 8, 16]) + s2: HashSet[int] = toSet([1, 2, 3, 5, 8]) + s3: HashSet[int] = toSet([3, 5, 7]) block union: let diff --git a/tests/stdlib/tcputime.nim b/tests/stdlib/tcputime.nim deleted file mode 100644 index b0cc19c6c..000000000 --- a/tests/stdlib/tcputime.nim +++ /dev/null @@ -1,11 +0,0 @@ -import times, os - -var e = epochTime() -var c = cpuTime() - -os.sleep(1000) - -e = epochTime() - e -c = cpuTime() - c - -echo "epochTime: ", e, " cpuTime: ", c diff --git a/tests/stdlib/thashes.nim b/tests/stdlib/thashes.nim index c442b43fb..2cd1e4e47 100644 --- a/tests/stdlib/thashes.nim +++ b/tests/stdlib/thashes.nim @@ -1,5 +1,14 @@ -import unittest -import hashes + +discard """ +output: ''' +[Suite] hashes + +[Suite] hashing + +''' +""" + +import unittest, hashes suite "hashes": suite "hashing": diff --git a/tests/stdlib/thttpcore.nim b/tests/stdlib/thttpcore.nim index 9f99df93a..889c734c5 100644 --- a/tests/stdlib/thttpcore.nim +++ b/tests/stdlib/thttpcore.nim @@ -1,3 +1,6 @@ +discard """ +output: "[Suite] httpcore" +""" import unittest diff --git a/tests/stdlib/tjsonexternproc.nim b/tests/stdlib/tjsonexternproc.nim index ec90e580d..1091d72cd 100644 --- a/tests/stdlib/tjsonexternproc.nim +++ b/tests/stdlib/tjsonexternproc.nim @@ -1,5 +1,11 @@ +discard """ +output: ''' +{"data":[1]} +''' +""" + # Test case for https://github.com/nim-lang/Nim/issues/6385 import mjsonexternproc # import json -foo(1) \ No newline at end of file +foo(1) diff --git a/tests/stdlib/tjsontestsuite.nim b/tests/stdlib/tjsontestsuite.nim index 06f783a73..db31963fd 100644 --- a/tests/stdlib/tjsontestsuite.nim +++ b/tests/stdlib/tjsontestsuite.nim @@ -1,3 +1,7 @@ +discard """ +disabled: true +""" + ## JSON tests based on https://github.com/nst/JSONTestSuite import unittest, diff --git a/tests/stdlib/tlists.nim b/tests/stdlib/tlists.nim index 37e73c53f..b7c7f9f5a 100644 --- a/tests/stdlib/tlists.nim +++ b/tests/stdlib/tlists.nim @@ -8,14 +8,14 @@ const data = [1, 2, 3, 4, 5, 6] block SinglyLinkedListTest1: - var L: TSinglyLinkedList[int] + var L: SinglyLinkedList[int] for d in items(data): L.prepend(d) assert($L == "[6, 5, 4, 3, 2, 1]") assert(4 in L) block SinglyLinkedListTest2: - var L: TSinglyLinkedList[string] + var L: SinglyLinkedList[string] for d in items(data): L.prepend($d) assert($L == """["6", "5", "4", "3", "2", "1"]""") @@ -23,7 +23,7 @@ block SinglyLinkedListTest2: block DoublyLinkedListTest1: - var L: TDoublyLinkedList[int] + var L: DoublyLinkedList[int] for d in items(data): L.prepend(d) for d in items(data): L.append(d) L.remove(L.find(1)) @@ -32,7 +32,7 @@ block DoublyLinkedListTest1: assert(4 in L) block SinglyLinkedRingTest1: - var L: TSinglyLinkedRing[int] + var L: SinglyLinkedRing[int] L.prepend(4) assert($L == "[4]") L.prepend(4) @@ -42,7 +42,7 @@ block SinglyLinkedRingTest1: block DoublyLinkedRingTest1: - var L: TDoublyLinkedRing[int] + var L: DoublyLinkedRing[int] L.prepend(4) assert($L == "[4]") L.prepend(4) diff --git a/tests/stdlib/tmath2.nim b/tests/stdlib/tmath2.nim deleted file mode 100644 index eb0506f5f..000000000 --- a/tests/stdlib/tmath2.nim +++ /dev/null @@ -1,85 +0,0 @@ -# tests for the interpreter - -proc loops(a: var int) = - discard - #var - # b: int - #b = glob - #while b != 0: - # b = b + 1 - #a = b - -proc mymax(a, b: int): int = - #loops(result) - result = a - if b > a: result = b - -proc test(a, b: int) = - var - x, y: int - x = 0 - y = 7 - if x == a + b * 3 - 7 or - x == 8 or - x == y and y > -56 and y < 699: - y = 0 - elif y == 78 and x == 0: - y = 1 - elif y == 0 and x == 0: - y = 2 - else: - y = 3 - -type - TTokType = enum - tkNil, tkType, tkConst, tkVar, tkSymbol, tkIf, - tkWhile, tkFor, tkLoop, tkCase, tkLabel, tkGoto - -proc testCase(t: TTokType): int = - case t - of tkNil, tkType, tkConst: result = 0 - of tkVar: result = 1 - of tkSymbol: result = 2 - of tkIf..tkFor: result = 3 - of tkLoop: result = 56 - else: result = -1 - test(0, 9) # test the call - -proc TestLoops() = - var - i, j: int - - while i >= 0: - if i mod 3 == 0: - break - i = i + 1 - while j == 13: - j = 13 - break - break - - while true: - break - - -var - glob: int - a: array[0..5, int] - -proc main() = - #glob = 0 - #loops( glob ) - var - res: int - s: string - #write(stdout, mymax(23, 45)) - write(stdout, "Hallo! Wie heisst du? ") - s = readLine(stdin) - # test the case statement - case s - of "Andreas": write(stdout, "Du bist mein Meister!\n") - of "Rumpf": write(stdout, "Du bist in der Familie meines Meisters!\n") - else: write(stdout, "ich kenne dich nicht!\n") - write(stdout, "Du heisst " & s & "\n") - -main() diff --git a/tests/stdlib/tmemfiles1.nim b/tests/stdlib/tmemfiles1.nim index 8b66dfcc1..a18fba083 100644 --- a/tests/stdlib/tmemfiles1.nim +++ b/tests/stdlib/tmemfiles1.nim @@ -1,5 +1,6 @@ discard """ file: "tmemfiles1.nim" + outputsub: "" """ import memfiles, os var @@ -8,5 +9,5 @@ var # Create a new file mm = memfiles.open(fn, mode = fmReadWrite, newFileSize = 20) mm.close() -mm.close() +# mm.close() if fileExists(fn): removeFile(fn) diff --git a/tests/stdlib/tmemlines.nim b/tests/stdlib/tmemlines.nim index c850b5493..98e03b5bb 100644 --- a/tests/stdlib/tmemlines.nim +++ b/tests/stdlib/tmemlines.nim @@ -1,3 +1,7 @@ +discard """ +outputsub: "" +""" + import memfiles var inp = memfiles.open("tests/stdlib/tmemlines.nim") for line in lines(inp): diff --git a/tests/stdlib/tmemlinesBuf.nim b/tests/stdlib/tmemlinesBuf.nim index 9fa68cf02..97ad751ee 100644 --- a/tests/stdlib/tmemlinesBuf.nim +++ b/tests/stdlib/tmemlinesBuf.nim @@ -1,6 +1,15 @@ +discard """ +output: "15" +disabled: "appveyor" +""" + import memfiles var inp = memfiles.open("tests/stdlib/tmemlinesBuf.nim") var buffer: TaintedString = "" +var lineCount = 0 for line in lines(inp, buffer): - echo("#" & line & "#") + lineCount += 1 + close(inp) + +echo lineCount diff --git a/tests/stdlib/tmemslices.nim b/tests/stdlib/tmemslices.nim index d724254a2..c0d6d3960 100644 --- a/tests/stdlib/tmemslices.nim +++ b/tests/stdlib/tmemslices.nim @@ -1,3 +1,9 @@ +discard """ +outputsub: "rlwuiadtrnzb" +""" + +# chatever the sub pattern it will find itself + import memfiles var inp = memfiles.open("tests/stdlib/tmemslices.nim") for mem in memSlices(inp): diff --git a/tests/stdlib/tnativesockets.nim b/tests/stdlib/tnativesockets.nim index c683647bc..c2738b8a5 100644 --- a/tests/stdlib/tnativesockets.nim +++ b/tests/stdlib/tnativesockets.nim @@ -1,3 +1,7 @@ +discard """ +outputsub: "" +""" + import nativesockets, unittest suite "nativesockets": @@ -5,4 +9,3 @@ suite "nativesockets": let hostname = getHostname() check hostname.len > 0 check hostname.len < 64 - diff --git a/tests/stdlib/tnet.nim b/tests/stdlib/tnet.nim index 009561272..2dd22796c 100644 --- a/tests/stdlib/tnet.nim +++ b/tests/stdlib/tnet.nim @@ -1,3 +1,7 @@ +discard """ +outputsub: "" +""" + import net, nativesockets import unittest diff --git a/tests/stdlib/tosprocterminate.nim b/tests/stdlib/tosprocterminate.nim index 7fc6c5d85..a46d91d68 100644 --- a/tests/stdlib/tosprocterminate.nim +++ b/tests/stdlib/tosprocterminate.nim @@ -1,3 +1,7 @@ +discard """ +outputsub: "SUCCESS" +""" + import os, osproc when defined(Windows): diff --git a/tests/stdlib/tparsopt.nim b/tests/stdlib/tparsopt.nim index 848fba2da..948bc8d5f 100644 --- a/tests/stdlib/tparsopt.nim +++ b/tests/stdlib/tparsopt.nim @@ -1,4 +1,10 @@ -# Test the new parseopt module +discard """ +disabled: true +""" + +# this file has a type in the name, and it does not really test +# parseopt module, because tester has no support to set arguments. Test the +# new parseopt module. Therefore it is disabled. import parseopt diff --git a/tests/stdlib/tposix.nim b/tests/stdlib/tposix.nim index 229035d22..14f1fd6e2 100644 --- a/tests/stdlib/tposix.nim +++ b/tests/stdlib/tposix.nim @@ -1,3 +1,7 @@ +discard """ +outputsub: "" +""" + # Test Posix interface when not defined(windows): @@ -5,7 +9,7 @@ when not defined(windows): import posix var - u: Tutsname + u: Utsname discard uname(u) @@ -13,4 +17,3 @@ when not defined(windows): writeLine(stdout, u.nodename) writeLine(stdout, u.release) writeLine(stdout, u.machine) - diff --git a/tests/stdlib/tquit.nim b/tests/stdlib/tquit.nim index d18b468c8..4f8d5fb20 100644 --- a/tests/stdlib/tquit.nim +++ b/tests/stdlib/tquit.nim @@ -1,3 +1,9 @@ +discard """ +output: ''' +just exiting... +''' +""" + # Test the new beforeQuit variable: proc myExit() {.noconv.} = diff --git a/tests/stdlib/trepr2.nim b/tests/stdlib/trepr2.nim index 300df565d..89379da96 100644 --- a/tests/stdlib/trepr2.nim +++ b/tests/stdlib/trepr2.nim @@ -1,3 +1,8 @@ +discard """ +outputsub: "" +""" + +# output not testable because repr prints pointer adresses # test the new "repr" built-in proc type diff --git a/tests/stdlib/trstgen.nim b/tests/stdlib/trstgen.nim index c702ccc2a..fd89f68af 100644 --- a/tests/stdlib/trstgen.nim +++ b/tests/stdlib/trstgen.nim @@ -1,3 +1,7 @@ +discard """ +outputsub: "" +""" + # tests for rstgen module. import ../../lib/packages/docutils/rstgen @@ -27,7 +31,7 @@ suite "YAML syntax highlighting": <span class="Punctuation">?</span> <span class="StringLit">key</span> <span class="Punctuation">:</span> <span class="StringLit">value</span> <span class="Keyword">...</span></pre>""" - + test "Block scalars": let input = """.. code-block:: yaml a literal block scalar: | @@ -55,7 +59,7 @@ suite "YAML syntax highlighting": <span class="StringLit">another literal block scalar</span><span class="Punctuation">:</span> <span class="Command">|+</span> <span class="Comment"># comment after header</span><span class="LongStringLit"> allowed, since more indented than parent</span></pre>""" - + test "Directives": let input = """.. code-block:: yaml %YAML 1.2 @@ -97,7 +101,7 @@ suite "YAML syntax highlighting": <span class="StringLit">more numbers</span><span class="Punctuation">:</span> <span class="Punctuation">[</span><span class="DecNumber">-783</span><span class="Punctuation">,</span> <span class="FloatNumber">11e78</span><span class="Punctuation">]</span><span class="Punctuation">,</span> <span class="StringLit">not numbers</span><span class="Punctuation">:</span> <span class="Punctuation">[</span> <span class="StringLit">42e</span><span class="Punctuation">,</span> <span class="StringLit">0023</span><span class="Punctuation">,</span> <span class="StringLit">+32.37</span><span class="Punctuation">,</span> <span class="StringLit">8 ball</span><span class="Punctuation">]</span> <span class="Punctuation">}</span></pre>""" - + test "Anchors, Aliases, Tags": let input = """.. code-block:: yaml --- !!map @@ -136,4 +140,4 @@ suite "YAML syntax highlighting": <span class="DecNumber">-3</span> <span class="DecNumber">-4</span> <span class="StringLit">example.com/not/a#comment</span><span class="Punctuation">:</span> - <span class="StringLit">?not a map key</span></pre>""" \ No newline at end of file + <span class="StringLit">?not a map key</span></pre>""" diff --git a/tests/stdlib/tsortcall.nim b/tests/stdlib/tsortcall.nim index 45b98805f..242e3fe4c 100644 --- a/tests/stdlib/tsortcall.nim +++ b/tests/stdlib/tsortcall.nim @@ -1,3 +1,7 @@ +discard """ +outputsub: "" +""" + import algorithm import unittest @@ -40,7 +44,7 @@ suite "test sort, sorted, and isSorted procs": test "test the shortcut versions with descending sort order": check(not unSortedIntSeq.isSorted(SortOrder.Descending)) check sorted(unSortedIntSeq, SortOrder.Descending) == reversed sortedIntSeq - check sorted(unSortedIntSeq).isSorted(SortOrder.Descending) + check sorted(unSortedIntSeq).isSorted(SortOrder.Ascending) unSortedIntSeq.sort(SortOrder.Descending) check unSortedIntSeq == reversed sortedIntSeq diff --git a/tests/stdlib/tstreams.nim b/tests/stdlib/tstreams.nim index 16dbc0e1b..559824d85 100644 --- a/tests/stdlib/tstreams.nim +++ b/tests/stdlib/tstreams.nim @@ -1,12 +1,25 @@ +discard """ +input: "Arne" +output: ''' +Hello! What is your name? +Nice name: Arne +fs is: nil + +threw exception +''' +disabled: "windows" +""" + + import streams block tstreams: var outp = newFileStream(stdout) var inp = newFileStream(stdin) - write(outp, "Hello! What is your name?") + writeLine(outp, "Hello! What is your name?") var line = readLine(inp) - write(outp, "Nice name: " & line) + writeLine(outp, "Nice name: " & line) block tstreams2: diff --git a/tests/stdlib/tstrtabs.nim b/tests/stdlib/tstrtabs.nim index a248cc3b2..18ed57167 100644 --- a/tests/stdlib/tstrtabs.nim +++ b/tests/stdlib/tstrtabs.nim @@ -1,3 +1,92 @@ +discard """ +sortoutput: true +output: ''' +key1: value1 +key2: value2 +key_0: value0 +key_10: value10 +key_11: value11 +key_12: value12 +key_13: value13 +key_14: value14 +key_15: value15 +key_16: value16 +key_17: value17 +key_18: value18 +key_19: value19 +key_20: value20 +key_21: value21 +key_22: value22 +key_23: value23 +key_24: value24 +key_25: value25 +key_26: value26 +key_27: value27 +key_28: value28 +key_29: value29 +key_30: value30 +key_31: value31 +key_32: value32 +key_33: value33 +key_34: value34 +key_35: value35 +key_36: value36 +key_37: value37 +key_38: value38 +key_39: value39 +key_3: value3 +key_40: value40 +key_41: value41 +key_42: value42 +key_43: value43 +key_44: value44 +key_45: value45 +key_46: value46 +key_47: value47 +key_48: value48 +key_49: value49 +key_4: value4 +key_50: value50 +key_51: value51 +key_52: value52 +key_53: value53 +key_54: value54 +key_55: value55 +key_56: value56 +key_57: value57 +key_58: value58 +key_59: value59 +key_5: value5 +key_60: value60 +key_61: value61 +key_62: value62 +key_63: value63 +key_64: value64 +key_65: value65 +key_66: value66 +key_67: value67 +key_68: value68 +key_69: value69 +key_6: value6 +key_70: value70 +key_71: value71 +key_72: value72 +key_73: value73 +key_74: value74 +key_75: value75 +key_76: value76 +key_77: value77 +key_78: value78 +key_79: value79 +key_7: value7 +key_80: value80 +key_8: value8 +key_9: value9 +length of table 81 +value1 = value2 +''' +""" + import strtabs var tab = newStringTable({"key1": "val1", "key2": "val2"}, @@ -9,4 +98,4 @@ for key, val in pairs(tab): writeLine(stdout, key, ": ", val) writeLine(stdout, "length of table ", $tab.len) -writeLine(stdout, `%`("$key1 = $key2; ${PATH}", tab, {useEnvironment})) +writeLine(stdout, `%`("$key1 = $key2", tab, {useEnvironment})) diff --git a/tests/stdlib/ttimes.nim b/tests/stdlib/ttimes.nim index 660c9325f..7ebbe61d9 100644 --- a/tests/stdlib/ttimes.nim +++ b/tests/stdlib/ttimes.nim @@ -84,6 +84,15 @@ template runTimezoneTests() = # formatting timezone as 'Z' for UTC parseTest("2001-01-12T22:04:05Z", "yyyy-MM-dd'T'HH:mm:ss" & tzFormat, "2001-01-12T22:04:05Z", 11) + # timezone offset formats + parseTest("2001-01-12T15:04:05 +7", "yyyy-MM-dd'T'HH:mm:ss z", + "2001-01-12T08:04:05Z", 11) + parseTest("2001-01-12T15:04:05 +07", "yyyy-MM-dd'T'HH:mm:ss zz", + "2001-01-12T08:04:05Z", 11) + parseTest("2001-01-12T15:04:05 +07:00", "yyyy-MM-dd'T'HH:mm:ss zzz", + "2001-01-12T08:04:05Z", 11) + parseTest("2001-01-12T15:04:05 +07:30:59", "yyyy-MM-dd'T'HH:mm:ss zzzz", + "2001-01-12T07:33:06Z", 11) # Kitchen = "3:04PM" parseTestTimeOnly("3:04PM", "h:mmtt", "15:04:00") diff --git a/tests/stdlib/twalker.nim b/tests/stdlib/twalker.nim deleted file mode 100644 index 91c97df01..000000000 --- a/tests/stdlib/twalker.nim +++ /dev/null @@ -1,13 +0,0 @@ -# iterate over all files with a given filter: - -import - "../../lib/pure/os.nim", ../../ lib / pure / times - -proc main(filter: string) = - for filename in walkFiles(filter): - writeLine(stdout, filename) - - for key, val in envPairs(): - writeLine(stdout, key & '=' & val) - -main("*.nim") diff --git a/tests/system/t7894.nim b/tests/system/t7894.nim index 2808e5020..27ee3f220 100644 --- a/tests/system/t7894.nim +++ b/tests/system/t7894.nim @@ -1,18 +1,24 @@ discard """ +disabled: "travis" +disabled: "appveyor" """ -import os +# CI integration servers are out of memory for this test const size = 250000000 -var saved = newSeq[seq[int8]]() -for i in 0..22: - # one of these is 0.25GB. - #echo i - var x = newSeq[int8](size) - sleep(10) - saved.add(x) +proc main() = -for x in saved: - #echo x.len - doAssert x.len == size + var saved = newSeq[seq[int8]]() + + for i in 0..22: + # one of these is 0.25GB. + #echo i + var x = newSeq[int8](size) + saved.add(x) + + for x in saved: + #echo x.len + doAssert x.len == size + +main() diff --git a/tests/system/talloc.nim b/tests/system/talloc.nim index bf2cd97a8..9b970fda0 100644 --- a/tests/system/talloc.nim +++ b/tests/system/talloc.nim @@ -1,6 +1,9 @@ discard """ +disabled: "appveyor" """ +# appveyor is "out of memory" + var x: ptr int x = cast[ptr int](alloc(7)) diff --git a/tests/system/talloc2.nim b/tests/system/talloc2.nim index 0757c0724..e40c3f93c 100644 --- a/tests/system/talloc2.nim +++ b/tests/system/talloc2.nim @@ -1,6 +1,9 @@ discard """ +disabled: "windows" """ +# appveyor is "out of memory" + const nmax = 2*1024*1024*1024 diff --git a/tests/system/tio.nim b/tests/system/tio.nim index 7e9e18950..c4d041560 100644 --- a/tests/system/tio.nim +++ b/tests/system/tio.nim @@ -1,14 +1,19 @@ discard """ +outputsub: "" +disabled: true """ import - unittest, osproc, streams, os, strformat + unittest, osproc, streams, os, strformat, strutils const STRING_DATA = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet." + const TEST_FILE = "tests/testdata/string.txt" proc echoLoop(str: string): string = result = "" - var process = startProcess(findExe("tests/system/helpers/readall_echo")) + let exe = findExe("tests/system/helpers/readall_echo") + echo "exe: ", exe + var process = startProcess(exe) var input = process.inputStream input.write(str) input.close() @@ -22,10 +27,9 @@ suite "io": test "stdin": check: echoLoop(STRING_DATA) == STRING_DATA - echoLoop(STRING_DATA[0..3999]) == STRING_DATA[0..3999] test "file": check: - readFile(TEST_FILE) == STRING_DATA + readFile(TEST_FILE).strip == STRING_DATA proc verifyFileSize(sz: int64) = diff --git a/tests/system/tnilconcats.nim b/tests/system/tnilconcats.nim index 5e4a1b317..c1126405c 100644 --- a/tests/system/tnilconcats.nim +++ b/tests/system/tnilconcats.nim @@ -23,3 +23,10 @@ when true: doAssert s == "fooabc" echo x + + # casting an empty string as sequence with shallow() should not segfault + var s2: string + shallow(s2) + s2 &= "foo" + doAssert s2 == "foo" + diff --git a/tests/system/tparams.nim b/tests/system/tparams.nim index dd5511b8f..015530043 100644 --- a/tests/system/tparams.nim +++ b/tests/system/tparams.nim @@ -1,6 +1,3 @@ -discard """ -""" - import os import osproc import parseopt2 @@ -13,7 +10,6 @@ if argv == @[]: doAssert execShellCmd(getAppFilename() & " \"foo bar\" --aa:bar=a --a=c:d --ab -c --a[baz]:doo") == 0 else: let f = toSeq(getopt()) - echo f.repr doAssert f[0].kind == cmdArgument and f[0].key == "foo bar" and f[0].val == "" doAssert f[1].kind == cmdLongOption and f[1].key == "aa" and f[1].val == "bar=a" doAssert f[2].kind == cmdLongOption and f[2].key == "a=c" and f[2].val == "d" diff --git a/tests/template/sunset.tmpl b/tests/template/sunset.nimf index 465b12a5e..465b12a5e 100644 --- a/tests/template/sunset.tmpl +++ b/tests/template/sunset.nimf diff --git a/tests/template/t2416.nim b/tests/template/t2416.nim deleted file mode 100644 index f73880718..000000000 --- a/tests/template/t2416.nim +++ /dev/null @@ -1,2 +0,0 @@ -import i2416 -i2416() diff --git a/tests/template/t2do.nim b/tests/template/t2do.nim deleted file mode 100644 index f5f6393dc..000000000 --- a/tests/template/t2do.nim +++ /dev/null @@ -1,23 +0,0 @@ -discard """ - output: "8.0" -""" - -# bug #2057 - -proc mpf_get_d(x: int): float = float(x) -proc mpf_cmp_d(a: int; b: float): int = 0 - -template toFloatHelper(result, tooSmall, tooLarge: untyped) = - result = mpf_get_d(a) - if result == 0.0 and mpf_cmp_d(a,0.0) != 0: - tooSmall - if result == Inf: - tooLarge - -proc toFloat*(a: int): float = - toFloatHelper(result) do: - raise newException(ValueError, "number too small") - do: - raise newException(ValueError, "number too large") - -echo toFloat(8) diff --git a/tests/template/tcan_access_hidden_field.nim b/tests/template/tcan_access_hidden_field.nim deleted file mode 100644 index a6f6490cc..000000000 --- a/tests/template/tcan_access_hidden_field.nim +++ /dev/null @@ -1,9 +0,0 @@ -discard """ - output: 33 -""" - -import mcan_access_hidden_field - -var myfoo = createFoo(33, 44) - -echo myfoo.geta diff --git a/tests/template/tconfusinglocal.nim b/tests/template/tconfusinglocal.nim index 50bf8f4b2..9f641e2bf 100644 --- a/tests/template/tconfusinglocal.nim +++ b/tests/template/tconfusinglocal.nim @@ -1,3 +1,7 @@ +discard """ +output: "0" +""" + # bug #5135 proc fail*[E](e: E): void = diff --git a/tests/template/tdefault_nil.nim b/tests/template/tdefault_nil.nim deleted file mode 100644 index 783f77388..000000000 --- a/tests/template/tdefault_nil.nim +++ /dev/null @@ -1,14 +0,0 @@ - -# bug #2629 -import sequtils, os - -template glob_rst(basedir: string = ""): untyped = - if baseDir.len == 0: - to_seq(walk_files("*.rst")) - else: - to_seq(walk_files(basedir/"*.rst")) - -let - rst_files = concat(glob_rst(), glob_rst("docs")) - -when isMainModule: echo rst_files diff --git a/tests/template/template_issues.nim b/tests/template/template_issues.nim new file mode 100644 index 000000000..8599b161a --- /dev/null +++ b/tests/template/template_issues.nim @@ -0,0 +1,209 @@ +discard """ +output: ''' +@[] +5 +0 +a +hi +''' +""" + + +import macros, json + + +block t2057: + proc mpf_get_d(x: int): float = float(x) + proc mpf_cmp_d(a: int; b: float): int = 0 + + template toFloatHelper(result, tooSmall, tooLarge: untyped) = + result = mpf_get_d(a) + if result == 0.0 and mpf_cmp_d(a,0.0) != 0: + tooSmall + if result == Inf: + tooLarge + + proc toFloat(a: int): float = + toFloatHelper(result) do: + raise newException(ValueError, "number too small") + do: + raise newException(ValueError, "number too large") + + doAssert toFloat(8) == 8.0 + + + +import sequtils, os +block t2629: + template glob_rst(basedir: string = ""): untyped = + if baseDir.len == 0: + to_seq(walk_files("*.rst")) + else: + to_seq(walk_files(basedir/"*.rst")) + + let rst_files = concat(glob_rst(), glob_rst("docs")) + + when isMainModule: echo rst_files + + +block t5417: + macro genBody: untyped = + let sbx = genSym(nskLabel, "test") + when true: + result = quote do: + block `sbx`: + break `sbx` + else: + template foo(s1, s2) = + block s1: + break s2 + result = getAst foo(sbx, sbx) + + proc test() = + genBody() + + + +block t909: + template baz() = + proc bar() = + var x = 5 + iterator foo(): int {.closure.} = + echo x + var y = foo + discard y() + + macro test(): untyped = + result = getAst(baz()) + + test() + bar() + + + +block t993: + type PNode = ref object of RootObj + + template litNode(name, ty) = + type name = ref object of PNode + val: ty + litNode PIntNode, int + + template withKey(j: JsonNode; key: string; varname, + body: untyped): typed = + if j.hasKey(key): + let varname{.inject.}= j[key] + block: + body + + var j = parsejson("{\"zzz\":1}") + withkey(j, "foo", x): + echo(x) + + + + +block t1337: + template someIt(a, pred): untyped = + var it {.inject.} = 0 + pred + + proc aProc(n: auto) = + n.someIt(echo(it)) + + aProc(89) + + + +import mlt +block t4564: + type Bar = ref object of RootObj + proc foo(a: Bar): int = 0 + var a: Bar + let b = a.foo() > 0 + + + +block t8052: + type + UintImpl[N: static[int], T: SomeUnsignedInt] = object + raw_data: array[N, T] + + template genLoHi(TypeImpl: untyped): untyped = + template loImpl[N: static[int], T: SomeUnsignedInt](dst: TypeImpl[N div 2, T], src: TypeImpl[N, T]) = + let halfSize = N div 2 + for i in 0 ..< halfSize: + dst.raw_data[i] = src.raw_data[i] + + proc lo[N: static[int], T: SomeUnsignedInt](x: TypeImpl[N,T]): TypeImpl[N div 2, T] {.inline.}= + loImpl(result, x) + + genLoHi(UintImpl) + + var a: UintImpl[4, uint32] + + a.raw_data = [1'u32, 2'u32, 3'u32, 4'u32] + doAssert a.lo.raw_data.len == 2 + doAssert a.lo.raw_data[0] == 1 + doAssert a.lo.raw_data[1] == 2 + + + +block t2585: + type + RenderPass = object + state: ref int + RenderData = object + fb: int + walls: seq[RenderPass] + Mat2 = int + Vector2[T] = T + Pixels=int + + template use(fb: int, st: untyped): untyped = + echo "a ", $fb + st + echo "a ", $fb + + proc render(rdat: var RenderData; passes: var openarray[RenderPass]; proj: Mat2; + indexType = 1) = + for i in 0 ..< len(passes): + echo "blah ", repr(passes[i]) + + proc render2(rdat: var RenderData; screenSz: Vector2[Pixels]; proj: Mat2) = + use rdat.fb: + render(rdat, rdat.walls, proj, 1) + + + +block t4292: + template foo(s: string): string = s + proc variadicProc(v: varargs[string, foo]) = echo v[0] + variadicProc("a") + + + +block t2670: + template testTemplate(b: bool): typed = + when b: + var a = "hi" + else: + var a = 5 + echo a + testTemplate(true) + + + +block t4097: + var i {.compileTime.} = 2 + + template defineId(t: typedesc) = + const id {.genSym.} = i + static: inc(i) + proc idFor(T: typedesc[t]): int {.inline, raises: [].} = id + + defineId(int8) + defineId(int16) + + doAssert idFor(int8) == 2 + doAssert idFor(int16) == 3 \ No newline at end of file diff --git a/tests/template/template_various.nim b/tests/template/template_various.nim new file mode 100644 index 000000000..029942621 --- /dev/null +++ b/tests/template/template_various.nim @@ -0,0 +1,251 @@ +discard """ +output: ''' +i2416 +33 +foo55 +foo8.0 +fooaha +bar7 +immediate +10 +4true +132 +''' +""" + +import macros + + + + +import i2416 +i2416() + + +import mcan_access_hidden_field +var myfoo = createFoo(33, 44) +echo myfoo.geta + + +import mgensym_generic_cross_module +foo(55) +foo 8.0 +foo "aha" +bar 7 + + + +block generic_templates: + type + SomeObj = object of RootObj + Foo[T, U] = object + x: T + y: U + + template someTemplate[T](): tuple[id: int32, obj: T] = + var result: tuple[id: int32, obj: T] = (0'i32, T()) + result + + let ret = someTemplate[SomeObj]() + + # https://github.com/nim-lang/Nim/issues/7829 + proc inner[T](): int = + discard + + template outer[A](): untyped = + inner[A]() + + template outer[B](x: int): untyped = + inner[B]() + + var i1 = outer[int]() + var i2 = outer[int](i1) + + # https://github.com/nim-lang/Nim/issues/7883 + template t1[T: int|int64](s: string): T = + var t: T + t + + template t1[T: int|int64](x: int, s: string): T = + var t: T + t + + var i3: int = t1[int]("xx") + + + +block tgetast_typeliar: + proc error(s: string) = quit s + + macro assertOrReturn(condition: bool; message: string): typed = + var line = condition.lineInfo() + result = quote do: + block: + if not likely(`condition`): + error("Assertion failed: " & $(`message`) & "\n" & `line`) + return + + macro assertOrReturn(condition: bool): typed = + var message = condition.toStrLit() + result = getAst assertOrReturn(condition, message) + + proc point(size: int16): tuple[x, y: int16] = + # returns random point in square area with given `size` + assertOrReturn size > 0 + + + +type + MyFloat = object + val: float +converter to_myfloat(x: float): MyFloat {.inline.} = + MyFloat(val: x) + +block pattern_with_converter: + proc `+`(x1, x2: MyFloat): MyFloat = + MyFloat(val: x1.val + x2.val) + + proc `*`(x1, x2: MyFloat): MyFloat = + MyFloat(val: x1.val * x2.val) + + template optMul{`*`(a, 2.0)}(a: MyFloat): MyFloat = + a + a + + func floatMyFloat(x: MyFloat): MyFloat = + result = x * 2.0 + + func floatDouble(x: float): float = + result = x * 2.0 + + doAssert floatDouble(5) == 10.0 + + + +block prefer_immediate: + # Test that immediate templates are preferred over non-immediate templates + + 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: untyped) {.immediate.} = echo "immediate" + template foo(a, b: bool) = echo "foo bool" + template foo(a, b: char) = echo "foo char" + + foo(undeclaredIdentifier, undeclaredIdentifier2) + + + + +block procparshadow: + template something(name: untyped) = + proc name(x: int) = + var x = x # this one should not be rejected by the compiler (#5225) + echo x + + something(what) + what(10) + + # bug #4750 + type + O = object + i: int + OP = ptr O + + template alf(p: pointer): untyped = + cast[OP](p) + + proc t1(al: pointer) = + var o = alf(al) + + proc t2(alf: pointer) = + var x = alf + var o = alf(x) + + + +block symchoicefield: + type Foo = object + len: int + + var f = Foo(len: 40) + + template getLen(f: Foo): int = f.len + + doAssert f.getLen == 40 + # This fails, because `len` gets the nkOpenSymChoice + # treatment inside the template early pass and then + # it can't be recognized as a field anymore + + + +import os, times +include "sunset.nimf" +block ttempl: + const + tabs = [["home", "index"], + ["news", "news"], + ["documentation", "documentation"], + ["download", "download"], + ["FAQ", "question"], + ["links", "links"]] + + + var i = 0 + for item in items(tabs): + var content = $i + var file: File + if open(file, changeFileExt(item[1], "html"), fmWrite): + write(file, sunsetTemplate(current=item[1], ticker="", content=content, + tabs=tabs)) + close(file) + else: + write(stdout, "cannot open file for writing") + inc(i) + + + +block ttempl4: + template `:=`(name, val: untyped): typed = + var name = val + + ha := 1 * 4 + hu := "ta-da" == "ta-da" + echo ha, hu + + + + +import mtempl5 +block ttempl5: + echo templ() + + #bug #892 + proc parse_to_close(value: string, index: int, open='(', close=')'): int = + discard + + # Call parse_to_close + template get_next_ident: typed = + discard "{something}".parse_to_close(0, open = '{', close = '}') + + get_next_ident() + + #identifier expected, but found '(open|open|open)' + #bug #880 (also example in the manual!) + template typedef(name: untyped, typ: typedesc) = + type + `T name` {.inject.} = typ + `P name` {.inject.} = ref `T name` + + typedef(myint, int) + var x: PMyInt + + + +block templreturntype: + template `=~` (a: int, b: int): bool = false + var foo = 2 =~ 3 + + + + diff --git a/tests/template/texponential_eval.nim b/tests/template/texponential_eval.nim index 32af9e8f7..b4e3faefb 100644 --- a/tests/template/texponential_eval.nim +++ b/tests/template/texponential_eval.nim @@ -1,13 +1,17 @@ # bug #1940 discard """ - nimout: '''=== +nimout: ''' +=== merge (A) with (B) merge (A B) with (C) merge (A B C) with (D) merge (A B C D) with (E) merge (A B C D E) with (F) -===''' +=== +''' + +output: "A B C D E F" """ type SqlStmt = tuple diff --git a/tests/template/tgenerictemplates.nim b/tests/template/tgenerictemplates.nim deleted file mode 100644 index 142505b1a..000000000 --- a/tests/template/tgenerictemplates.nim +++ /dev/null @@ -1,37 +0,0 @@ -type - SomeObj = object of RootObj - - Foo[T, U] = object - x: T - y: U - -template someTemplate[T](): tuple[id: int32, obj: T] = - var result: tuple[id: int32, obj: T] = (0'i32, T()) - result - -let ret = someTemplate[SomeObj]() - -# https://github.com/nim-lang/Nim/issues/7829 -proc inner*[T](): int = - discard - -template outer*[A](): untyped = - inner[A]() - -template outer*[B](x: int): untyped = - inner[B]() - -var i1 = outer[int]() -var i2 = outer[int](i1) - -# https://github.com/nim-lang/Nim/issues/7883 -template t1[T: int|int64](s: string): T = - var t: T - t - -template t1[T: int|int64](x: int, s: string): T = - var t: T - t - -var i3: int = t1[int]("xx") - diff --git a/tests/template/tgensym_generic_cross_module.nim b/tests/template/tgensym_generic_cross_module.nim deleted file mode 100644 index 856ab676d..000000000 --- a/tests/template/tgensym_generic_cross_module.nim +++ /dev/null @@ -1,14 +0,0 @@ -discard """ - output: '''foo55 -foo8.0 -fooaha -bar7''' -""" -# bug #5419 -import mgensym_generic_cross_module - -foo(55) -foo 8.0 -foo "aha" -bar 7 - diff --git a/tests/template/tgensym_label.nim b/tests/template/tgensym_label.nim deleted file mode 100644 index fd3b0a1ee..000000000 --- a/tests/template/tgensym_label.nim +++ /dev/null @@ -1,18 +0,0 @@ - -# bug #5417 -import macros - -macro genBody: untyped = - let sbx = genSym(nskLabel, "test") - when true: - result = quote do: - block `sbx`: - break `sbx` - else: - template foo(s1, s2) = - block s1: - break s2 - result = getAst foo(sbx, sbx) - -proc test() = - genBody() diff --git a/tests/template/tgetast_typeliar.nim b/tests/template/tgetast_typeliar.nim deleted file mode 100644 index c9a612582..000000000 --- a/tests/template/tgetast_typeliar.nim +++ /dev/null @@ -1,23 +0,0 @@ - -# just ensure this keeps compiling: - -import macros - -proc error(s: string) = quit s - -macro assertOrReturn*(condition: bool; message: string): typed = - var line = condition.lineInfo() - result = quote do: - block: - if not likely(`condition`): - error("Assertion failed: " & $(`message`) & "\n" & `line`) - return - -macro assertOrReturn*(condition: bool): typed = - var message = condition.toStrLit() - result = getAst assertOrReturn(condition, message) - -proc point*(size: int16): tuple[x, y: int16] = - # returns random point in square area with given `size` - - assertOrReturn size > 0 diff --git a/tests/template/thygienictempl.nim b/tests/template/thygienictempl.nim index de40450aa..506f57148 100644 --- a/tests/template/thygienictempl.nim +++ b/tests/template/thygienictempl.nim @@ -1,3 +1,7 @@ +discard """ +action: compile +""" + var e = "abc" diff --git a/tests/template/tissue909.nim b/tests/template/tissue909.nim deleted file mode 100644 index 6786ff48c..000000000 --- a/tests/template/tissue909.nim +++ /dev/null @@ -1,16 +0,0 @@ -import macros - -template baz() = - proc bar() = - var x = 5 - iterator foo(): int {.closure.} = - echo x - var y = foo - discard y() - -macro test(): untyped = - result = getAst(baz()) - echo(treeRepr(result)) - -test() -bar() diff --git a/tests/template/tissue993.nim b/tests/template/tissue993.nim deleted file mode 100644 index 552890bb4..000000000 --- a/tests/template/tissue993.nim +++ /dev/null @@ -1,20 +0,0 @@ - -type PNode* = ref object of RootObj - -template litNode(name, ty) = - type name* = ref object of PNode - val*: ty -litNode PIntNode, int - -import json - -template withKey*(j: JsonNode; key: string; varname, - body: untyped): typed = - if j.hasKey(key): - let varname{.inject.}= j[key] - block: - body - -var j = parsejson("{\"zzz\":1}") -withkey(j, "foo", x): - echo(x) diff --git a/tests/template/tit.nim b/tests/template/tit.nim deleted file mode 100644 index 76b1d151b..000000000 --- a/tests/template/tit.nim +++ /dev/null @@ -1,11 +0,0 @@ - -# bug #1337 - -template someIt(a, pred): untyped = - var it {.inject.} = 0 - pred - -proc aProc(n: auto) = - n.someIt(echo(it)) - -aProc(89) diff --git a/tests/template/tlt.nim b/tests/template/tlt.nim deleted file mode 100644 index 75c7dd991..000000000 --- a/tests/template/tlt.nim +++ /dev/null @@ -1,7 +0,0 @@ - -import mlt -# bug #4564 -type Bar* = ref object of RootObj -proc foo(a: Bar): int = 0 -var a: Bar -let b = a.foo() > 0 diff --git a/tests/template/tnested_template.nim b/tests/template/tnested_template.nim deleted file mode 100644 index 37166009d..000000000 --- a/tests/template/tnested_template.nim +++ /dev/null @@ -1,23 +0,0 @@ -# bug #8052 - -type - UintImpl*[N: static[int], T: SomeUnsignedInt] = object - raw_data*: array[N, T] - -template genLoHi(TypeImpl: untyped): untyped = - template loImpl[N: static[int], T: SomeUnsignedInt](dst: TypeImpl[N div 2, T], src: TypeImpl[N, T]) = - let halfSize = N div 2 - for i in 0 ..< halfSize: - dst.raw_data[i] = src.raw_data[i] - - proc lo*[N: static[int], T: SomeUnsignedInt](x: TypeImpl[N,T]): TypeImpl[N div 2, T] {.inline.}= - loImpl(result, x) - -genLoHi(UintImpl) - -var a: UintImpl[4, uint32] - -a.raw_data = [1'u32, 2'u32, 3'u32, 4'u32] -assert a.lo.raw_data.len == 2 -assert a.lo.raw_data[0] == 1 -assert a.lo.raw_data[1] == 2 diff --git a/tests/template/tparams_gensymed.nim b/tests/template/tparams_gensymed.nim index 3fb0dd4a5..da86d63dc 100644 --- a/tests/template/tparams_gensymed.nim +++ b/tests/template/tparams_gensymed.nim @@ -1,4 +1,15 @@ - +discard """ +output: ''' +0 +1 +2 +3 +0 +1 +2 +3 +''' +""" # bug #1915 import macros diff --git a/tests/template/tpattern_with_converter.nim b/tests/template/tpattern_with_converter.nim deleted file mode 100644 index e0632552b..000000000 --- a/tests/template/tpattern_with_converter.nim +++ /dev/null @@ -1,27 +0,0 @@ -discard """ - output: 10.0 -""" - -type - MyFloat = object - val: float - -converter to_myfloat*(x: float): MyFloat {.inline.} = - MyFloat(val: x) - -proc `+`(x1, x2: MyFloat): MyFloat = - MyFloat(val: x1.val + x2.val) - -proc `*`(x1, x2: MyFloat): MyFloat = - MyFloat(val: x1.val * x2.val) - -template optMul{`*`(a, 2.0)}(a: MyFloat): MyFloat = - a + a - -func floatMyFloat(x: MyFloat): MyFloat = - result = x * 2.0 - -func floatDouble(x: float): float = - result = x * 2.0 - -echo floatDouble(5) \ No newline at end of file diff --git a/tests/template/tprefer_immediate.nim b/tests/template/tprefer_immediate.nim deleted file mode 100644 index 3a4cfc07b..000000000 --- a/tests/template/tprefer_immediate.nim +++ /dev/null @@ -1,15 +0,0 @@ -discard """ - output: '''immediate''' -""" - -# Test that immediate templates are preferred over non-immediate templates - -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: 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/tprocparshadow.nim b/tests/template/tprocparshadow.nim deleted file mode 100644 index de1c2d941..000000000 --- a/tests/template/tprocparshadow.nim +++ /dev/null @@ -1,30 +0,0 @@ -discard """ - output: "10" -""" - -template something(name: untyped) = - proc name(x: int) = - var x = x # this one should not be rejected by the compiler (#5225) - echo x - -something(what) -what(10) - -# bug #4750 - -type - O = object - i: int - - OP = ptr O - -template alf(p: pointer): untyped = - cast[OP](p) - - -proc t1(al: pointer) = - var o = alf(al) - -proc t2(alf: pointer) = - var x = alf - var o = alf(x) diff --git a/tests/template/tsighash_regression.nim b/tests/template/tsighash_regression.nim index bf1f4dfe4..f3a6b4833 100644 --- a/tests/template/tsighash_regression.nim +++ b/tests/template/tsighash_regression.nim @@ -1,5 +1,8 @@ +discard """ +exitcode: 1 +outputsub: "0" +""" import tconfusinglocal - fail "foo" diff --git a/tests/template/tstmt_semchecked_twice.nim b/tests/template/tstmt_semchecked_twice.nim deleted file mode 100644 index c6463ae06..000000000 --- a/tests/template/tstmt_semchecked_twice.nim +++ /dev/null @@ -1,30 +0,0 @@ - -# bug #2585 - -type - RenderPass = object - state: ref int - - RenderData* = object - fb: int - walls: seq[RenderPass] - - Mat2 = int - Vector2[T] = T - Pixels=int - -template use*(fb: int, st: untyped): untyped = - echo "a ", $fb - st - echo "a ", $fb - -proc render(rdat: var RenderData; passes: var openarray[RenderPass]; proj: Mat2; - indexType = 1) = - for i in 0 .. <len(passes): - echo "blah ", repr(passes[i]) - - - -proc render2*(rdat: var RenderData; screenSz: Vector2[Pixels]; proj: Mat2) = - use rdat.fb: - render(rdat, rdat.walls, proj, 1) diff --git a/tests/template/tsymchoicefield.nim b/tests/template/tsymchoicefield.nim deleted file mode 100644 index 4483c2aa2..000000000 --- a/tests/template/tsymchoicefield.nim +++ /dev/null @@ -1,11 +0,0 @@ -type Foo = object - len: int - -var f = Foo(len: 40) - -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/ttemp_in_varargs.nim b/tests/template/ttemp_in_varargs.nim deleted file mode 100644 index be78e6ef2..000000000 --- a/tests/template/ttemp_in_varargs.nim +++ /dev/null @@ -1,9 +0,0 @@ -discard """ - output: '''a''' -""" - -# bug #4292 - -template foo(s: string): string = s -proc variadicProc*(v: varargs[string, foo]) = echo v[0] -variadicProc("a") diff --git a/tests/template/ttempl.nim b/tests/template/ttempl.nim deleted file mode 100644 index c022201f2..000000000 --- a/tests/template/ttempl.nim +++ /dev/null @@ -1,27 +0,0 @@ -# Test the new template file mechanism - -import - os, times - -include "sunset.tmpl" - -const - tabs = [["home", "index"], - ["news", "news"], - ["documentation", "documentation"], - ["download", "download"], - ["FAQ", "question"], - ["links", "links"]] - - -var i = 0 -for item in items(tabs): - var content = $i - var file: File - if open(file, changeFileExt(item[1], "html"), fmWrite): - write(file, sunsetTemplate(current=item[1], ticker="", content=content, - tabs=tabs)) - close(file) - else: - write(stdout, "cannot open file for writing") - inc(i) diff --git a/tests/template/ttempl3.nim b/tests/template/ttempl3.nim index d6a369d32..943cdae05 100644 --- a/tests/template/ttempl3.nim +++ b/tests/template/ttempl3.nim @@ -1,3 +1,7 @@ +discard """ +action: compile +""" + template withOpenFile(f: untyped, filename: string, mode: FileMode, actions: untyped): untyped = diff --git a/tests/template/ttempl4.nim b/tests/template/ttempl4.nim deleted file mode 100644 index d1d26385f..000000000 --- a/tests/template/ttempl4.nim +++ /dev/null @@ -1,7 +0,0 @@ - -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 deleted file mode 100644 index fd3ea0cad..000000000 --- a/tests/template/ttempl5.nim +++ /dev/null @@ -1,28 +0,0 @@ - -import mtempl5 - -echo templ() - -#bug #892 - -proc parse_to_close(value: string, index: int, open='(', close=')'): int = - discard - -# Call parse_to_close -template get_next_ident: typed = - discard "{something}".parse_to_close(0, open = '{', close = '}') - -get_next_ident() - - -#identifier expected, but found '(open|open|open)' - -#bug #880 (also example in the manual!) - -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/ttemplreturntype.nim b/tests/template/ttemplreturntype.nim deleted file mode 100644 index 642fa1b72..000000000 --- a/tests/template/ttemplreturntype.nim +++ /dev/null @@ -1,4 +0,0 @@ - -template `=~` (a: int, b: int): bool = false -var foo = 2 =~ 3 - diff --git a/tests/template/twhen_gensym.nim b/tests/template/twhen_gensym.nim deleted file mode 100644 index f1a8d0eb7..000000000 --- a/tests/template/twhen_gensym.nim +++ /dev/null @@ -1,13 +0,0 @@ -discard """ - output: "hi" -""" - -# bug #2670 -template testTemplate(b: bool): typed = - when b: - var a = "hi" - else: - var a = 5 - echo a - -testTemplate(true) diff --git a/tests/template/typedescids.nim b/tests/template/typedescids.nim deleted file mode 100644 index 1df2f69fb..000000000 --- a/tests/template/typedescids.nim +++ /dev/null @@ -1,17 +0,0 @@ -discard """ - output: '''2 3''' -""" - -# bug #4097 - -var i {.compileTime.} = 2 - -template defineId*(t: typedesc) = - const id {.genSym.} = i - static: inc(i) - proc idFor*(T: typedesc[t]): int {.inline, raises: [].} = id - -defineId(int8) -defineId(int16) - -echo idFor(int8), " ", idFor(int16) diff --git a/tests/test_nimscript.nims b/tests/test_nimscript.nims index d3eb9808e..b9a6097c2 100644 --- a/tests/test_nimscript.nims +++ b/tests/test_nimscript.nims @@ -9,7 +9,7 @@ import lists import math # import marshal import options -import ospaths +import os # import parsecfg # import parseopt import parseutils diff --git a/tests/threads/tactors2.nim b/tests/threads/tactors2.nim index b011ef140..e8afe203c 100644 --- a/tests/threads/tactors2.nim +++ b/tests/threads/tactors2.nim @@ -12,7 +12,7 @@ proc thread_proc(input: some_type): some_type {.thread.} = result.bla = 1 proc main() = - var actorPool: TActorPool[some_type, some_type] + var actorPool: ActorPool[some_type, some_type] createActorPool(actorPool, 1) var some_data: some_type diff --git a/tests/trmacros/targlist.nim b/tests/trmacros/targlist.nim deleted file mode 100644 index 46235dab1..000000000 --- a/tests/trmacros/targlist.nim +++ /dev/null @@ -1,9 +0,0 @@ -discard """ - output: "12false3ha" -""" - -proc f(x: varargs[string, `$`]) = discard -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 deleted file mode 100644 index 315570d8f..000000000 --- a/tests/trmacros/tcse.nim +++ /dev/null @@ -1,13 +0,0 @@ -discard """ - output: "4" -""" - -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 - -var - a: array[0..10, int] - i = 3 -echo a[i] + a[i] diff --git a/tests/trmacros/thoist.nim b/tests/trmacros/thoist.nim deleted file mode 100644 index 657f210a1..000000000 --- a/tests/trmacros/thoist.nim +++ /dev/null @@ -1,13 +0,0 @@ -discard """ - output: '''true -true''' -""" - -import pegs - -template optPeg{peg(pattern)}(pattern: string{lit}): Peg = - var gl {.global, gensym.} = peg(pattern) - gl - -echo match("(a b c)", peg"'(' @ ')'") -echo match("W_HI_Le", peg"\y 'while'") diff --git a/tests/trmacros/tmatrix.nim b/tests/trmacros/tmatrix.nim deleted file mode 100644 index a14ad2db0..000000000 --- a/tests/trmacros/tmatrix.nim +++ /dev/null @@ -1,29 +0,0 @@ -discard """ - output: "21" -""" - -import macros - -type - TMat = object - dummy: int - -proc `*`(a, b: TMat): TMat = nil -proc `+`(a, b: TMat): TMat = nil -proc `-`(a, b: TMat): TMat = nil -proc `$`(a: TMat): string = result = $a.dummy -proc mat21(): TMat = - result.dummy = 21 - -macro optOps{ (`+`|`-`|`*`) ** a }(a: TMat): untyped = - echo treeRepr(a) - result = newCall(bindSym"mat21") - -#macro optPlus{ `+` * a }(a: varargs[TMat]): expr = -# result = newIntLitNode(21) - -var x, y, z: TMat - -echo x + y * z - x - -#echo x + y + z diff --git a/tests/trmacros/tnoalias.nim b/tests/trmacros/tnoalias.nim deleted file mode 100644 index ec12d4712..000000000 --- a/tests/trmacros/tnoalias.nim +++ /dev/null @@ -1,16 +0,0 @@ -discard """ - output: "23" -""" - -template optslice{a = b + c}(a: untyped{noalias}, b, c: untyped): typed = - a = b - inc a, c - -var - x = 12 - y = 10 - z = 13 - -x = y+z - -echo x diff --git a/tests/trmacros/tnoalias2.nim b/tests/trmacros/tnoalias2.nim deleted file mode 100644 index 9362e764f..000000000 --- a/tests/trmacros/tnoalias2.nim +++ /dev/null @@ -1,19 +0,0 @@ -discard """ - output: '''0''' -""" - -# bug #206 -template optimizeOut{testFunc(a, b)}(a: int, b: int{alias}): untyped = 0 - -proc testFunc(a, b: int): int = result = a + b -var testVar = 1 -echo testFunc(testVar, testVar) - - -template ex{a = b + c}(a : int{noalias}, b, c : int) = - a = b - inc a, b - echo "came here" - -var x = 5 -x = x + x diff --git a/tests/trmacros/tnoendlessrec.nim b/tests/trmacros/tnoendlessrec.nim deleted file mode 100644 index 508770ca7..000000000 --- a/tests/trmacros/tnoendlessrec.nim +++ /dev/null @@ -1,10 +0,0 @@ -discard """ - output: "4" -""" - -# test that an endless recursion is avoided: - -template optLen{len(x)}(x: typed): int = len(x) - -var s = "lala" -echo len(s) diff --git a/tests/trmacros/tpartial.nim b/tests/trmacros/tpartial.nim deleted file mode 100644 index c636684d7..000000000 --- a/tests/trmacros/tpartial.nim +++ /dev/null @@ -1,11 +0,0 @@ -discard """ - output: '''-2''' -""" - -proc p(x, y: int; cond: bool): int = - result = if cond: x + y else: 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/tpatterns.nim b/tests/trmacros/tpatterns.nim deleted file mode 100644 index 907973637..000000000 --- a/tests/trmacros/tpatterns.nim +++ /dev/null @@ -1,23 +0,0 @@ -discard """ - output: '''48 -hel -lo''' -""" - -template optZero{x+x}(x: int): int = x*3 -template andthen{`*`(x,3)}(x: int): int = x*4 -template optSubstr1{x = substr(x, a, b)}(x: string, a, b: int) = setlen(x, b+1) - -var y = 12 -echo y+y - -var s: array[0..2, string] -s[0] = "hello" -s[0] = substr(s[0], 0, 2) - -echo s[0] - -# Test varargs matching -proc someVarargProc(k: varargs[string]) = doAssert(false) # this should not get called -template someVarargProcSingleArg{someVarargProc([a])}(a: string) = echo a -someVarargProc("lo") diff --git a/tests/trmacros/trmacros_various.nim b/tests/trmacros/trmacros_various.nim new file mode 100644 index 000000000..74b248739 --- /dev/null +++ b/tests/trmacros/trmacros_various.nim @@ -0,0 +1,110 @@ +discard """ +output: ''' +12false3ha +21 +optimized +''' +""" + +import macros, pegs + + +block arglist: + proc f(x: varargs[string, `$`]) = discard + template optF{f(x)}(x: varargs[untyped]) = + writeLine(stdout, x) + + f 1, 2, false, 3, "ha" + + + +block tcse: + 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 + + var + a: array[0..10, int] + i = 3 + doAssert a[i] + a[i] == 4 + + + +block hoist: + template optPeg{peg(pattern)}(pattern: string{lit}): Peg = + var gl {.global, gensym.} = peg(pattern) + gl + doAssert match("(a b c)", peg"'(' @ ')'") + doAssert match("W_HI_Le", peg"\y 'while'") + + + +block tmatrix: + type + TMat = object + dummy: int + + proc `*`(a, b: TMat): TMat = nil + proc `+`(a, b: TMat): TMat = nil + proc `-`(a, b: TMat): TMat = nil + proc `$`(a: TMat): string = result = $a.dummy + proc mat21(): TMat = + result.dummy = 21 + + macro optOps{ (`+`|`-`|`*`) ** a }(a: TMat): untyped = + result = newCall(bindSym"mat21") + + #macro optPlus{ `+` * a }(a: varargs[TMat]): expr = + # result = newIntLitNode(21) + + var x, y, z: TMat + echo x + y * z - x + + + +block tnoalias: + template optslice{a = b + c}(a: untyped{noalias}, b, c: untyped): typed = + a = b + inc a, c + var + x = 12 + y = 10 + z = 13 + x = y+z + doAssert x == 23 + + + +block tnoendlessrec: + # test that an endless recursion is avoided: + template optLen{len(x)}(x: typed): int = len(x) + + var s = "lala" + doAssert len(s) == 4 + + + +block tstatic_t_bug: + # bug #4227 + type Vector64[N: static[int]] = array[N, int] + + proc `*`[N: static[int]](a: Vector64[N]; b: float64): Vector64[N] = + result = a + + proc `+=`[N: static[int]](a: var Vector64[N]; b: Vector64[N]) = + echo "regular" + + proc linearCombinationMut[N: static[int]](a: float64, v: var Vector64[N], w: Vector64[N]) {. inline .} = + echo "optimized" + + template rewriteLinearCombinationMut{v += `*`(w, a)}(a: float64, v: var Vector64, w: Vector64): auto = + linearCombinationMut(a, v, w) + + proc main() = + const scaleVal = 9.0 + var a, b: Vector64[7] + a += b * scaleval + + main() + diff --git a/tests/trmacros/trmacros_various2.nim b/tests/trmacros/trmacros_various2.nim new file mode 100644 index 000000000..d500c49de --- /dev/null +++ b/tests/trmacros/trmacros_various2.nim @@ -0,0 +1,79 @@ +discard """ +output: ''' +0 +-2 +48 +hel +lo +my awesome concat +''' +""" + + +block tnoalias2: + # bug #206 + template optimizeOut{testFunc(a, b)}(a: int, b: int{alias}): untyped = 0 + + proc testFunc(a, b: int): int = result = a + b + var testVar = 1 + echo testFunc(testVar, testVar) + + + template ex{a = b + c}(a : int{noalias}, b, c : int) = + a = b + inc a, b + echo "came here" + + var x = 5 + x = x + x + + + +block tpartial: + proc p(x, y: int; cond: bool): int = + result = if cond: x + y else: 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) + + + +block tpatterns: + template optZero{x+x}(x: int): int = x*3 + template andthen{`*`(x,3)}(x: int): int = x*4 + template optSubstr1{x = substr(x, a, b)}(x: string, a, b: int) = setlen(x, b+1) + + var y = 12 + echo y+y + + var s: array[0..2, string] + s[0] = "hello" + s[0] = substr(s[0], 0, 2) + + echo s[0] + + # Test varargs matching + proc someVarargProc(k: varargs[string]) = doAssert(false) # this should not get called + template someVarargProcSingleArg{someVarargProc([a])}(a: string) = echo a + someVarargProc("lo") + + + +block tstar: + var + calls = 0 + + proc `&&`(s: varargs[string]): string = + result = s[0] + for i in 1..len(s)-1: result.add s[i] + inc calls + + template optConc{ `&&` * a }(a: string): string = &&a + + let space = " " + echo "my" && (space & "awe" && "some " ) && "concat" + + # check that it's been optimized properly: + doAssert calls == 1 diff --git a/tests/trmacros/tstar.nim b/tests/trmacros/tstar.nim deleted file mode 100644 index 86f698232..000000000 --- a/tests/trmacros/tstar.nim +++ /dev/null @@ -1,19 +0,0 @@ -discard """ - output: "my awesome concat" -""" - -var - calls = 0 - -proc `&&`(s: varargs[string]): string = - result = s[0] - for i in 1..len(s)-1: result.add s[i] - inc calls - -template optConc{ `&&` * a }(a: string): string = &&a - -let space = " " -echo "my" && (space & "awe" && "some " ) && "concat" - -# check that it's been optimized properly: -doAssert calls == 1 diff --git a/tests/trmacros/tstatic_t_bug.nim b/tests/trmacros/tstatic_t_bug.nim deleted file mode 100644 index cdfa53514..000000000 --- a/tests/trmacros/tstatic_t_bug.nim +++ /dev/null @@ -1,24 +0,0 @@ -discard """ - output: "optimized" -""" -# bug #4227 -type Vector64[N: static[int]] = array[N, int] - -proc `*`*[N: static[int]](a: Vector64[N]; b: float64): Vector64[N] = - result = a - -proc `+=`*[N: static[int]](a: var Vector64[N]; b: Vector64[N]) = - echo "regular" - -proc linearCombinationMut[N: static[int]](a: float64, v: var Vector64[N], w: Vector64[N]) {. inline .} = - echo "optimized" - -template rewriteLinearCombinationMut*{v += `*`(w, a)}(a: float64, v: var Vector64, w: Vector64): auto = - linearCombinationMut(a, v, w) - -proc main() = - const scaleVal = 9.0 - var a, b: Vector64[7] - a += b * scaleval - -main() diff --git a/tests/tuples/tanontuples.nim b/tests/tuples/tanontuples.nim deleted file mode 100644 index f514670d3..000000000 --- a/tests/tuples/tanontuples.nim +++ /dev/null @@ -1,26 +0,0 @@ -discard """ - output: '''61, 125 -(Field0: 0) (Field0: 13)''' -""" - -import macros - -proc `^` (a, b: int): int = - result = 1 - for i in 1..b: result = result * a - -var m = (0, 5) -var n = (56, 3) - -m = (n[0] + m[1], m[1] ^ n[1]) - -echo m[0], ", ", m[1] - -# also test we can produce unary anon tuples in a macro: -macro mm(): untyped = - result = newTree(nnkTupleConstr, newLit(13)) - -proc nowTuple(): (int,) = - result = (0,) - -echo nowTuple(), " ", mm() diff --git a/tests/tuples/tconver_tuple.nim b/tests/tuples/tconver_tuple.nim deleted file mode 100644 index 306da77fe..000000000 --- a/tests/tuples/tconver_tuple.nim +++ /dev/null @@ -1,23 +0,0 @@ -# Bug 4479 - -type - MyTuple = tuple - num: int - strings: seq[string] - ints: seq[int] - -var foo = MyTuple(( - num: 7, - strings: @[], - ints: @[], -)) - -var bar = ( - num: 7, - strings: @[], - ints: @[], -).MyTuple - -var fooUnnamed = MyTuple((7, @[], @[])) -var n = 7 -var fooSym = MyTuple((num: n, strings: @[], ints: @[])) diff --git a/tests/tuples/tdifferent_instantiations.nim b/tests/tuples/tdifferent_instantiations.nim deleted file mode 100644 index 93b1777b5..000000000 --- a/tests/tuples/tdifferent_instantiations.nim +++ /dev/null @@ -1,9 +0,0 @@ -# bug #1910 -import tables - -var p: OrderedTable[tuple[a:int], int] -var q: OrderedTable[tuple[x:int], int] -for key in p.keys: - echo key.a -for key in q.keys: - echo key.x diff --git a/tests/tuples/tgeneric_tuple.nim b/tests/tuples/tgeneric_tuple.nim deleted file mode 100644 index 32f081596..000000000 --- a/tests/tuples/tgeneric_tuple.nim +++ /dev/null @@ -1,9 +0,0 @@ -# bug #2121 - -type - Item[K,V] = tuple - key: K - value: V - -var q = newseq[Item[int,int]](0) -let (x,y) = q[0] diff --git a/tests/tuples/tgeneric_tuple2.nim b/tests/tuples/tgeneric_tuple2.nim deleted file mode 100644 index c0c292388..000000000 --- a/tests/tuples/tgeneric_tuple2.nim +++ /dev/null @@ -1,17 +0,0 @@ - -# bug #2369 - -type HashedElem[T] = tuple[num: int, storedVal: ref T] - -proc append[T](tab: var seq[HashedElem[T]], n: int, val: ref T) = - #tab.add((num: n, storedVal: val)) - var he: HashedElem[T] = (num: n, storedVal: val) - #tab.add(he) - -var g: seq[HashedElem[int]] = @[] - -proc foo() = - var x: ref int - new(x) - x[] = 77 - g.append(44, x) diff --git a/tests/tuples/ttuples_issues.nim b/tests/tuples/ttuples_issues.nim new file mode 100644 index 000000000..9380bd027 --- /dev/null +++ b/tests/tuples/ttuples_issues.nim @@ -0,0 +1,77 @@ +discard """ +output: ''' +''' +""" + + +import tables + + +block t4479: + type + MyTuple = tuple + num: int + strings: seq[string] + ints: seq[int] + + var foo = MyTuple(( + num: 7, + strings: @[], + ints: @[], + )) + + var bar = ( + num: 7, + strings: @[], + ints: @[], + ).MyTuple + + var fooUnnamed = MyTuple((7, @[], @[])) + var n = 7 + var fooSym = MyTuple((num: n, strings: @[], ints: @[])) + + +block t1910: + var p = newOrderedTable[tuple[a:int], int]() + var q = newOrderedTable[tuple[x:int], int]() + for key in p.keys: + echo key.a + for key in q.keys: + echo key.x + + +block t2121: + type + Item[K,V] = tuple + key: K + value: V + + var q = newseq[Item[int,int]](1) + let (x,y) = q[0] + + +block t2369: + type HashedElem[T] = tuple[num: int, storedVal: ref T] + + proc append[T](tab: var seq[HashedElem[T]], n: int, val: ref T) = + #tab.add((num: n, storedVal: val)) + var he: HashedElem[T] = (num: n, storedVal: val) + #tab.add(he) + + var g: seq[HashedElem[int]] = @[] + + proc foo() = + var x: ref int + new(x) + x[] = 77 + g.append(44, x) + + +block t1986: + proc test(): int64 = + return 0xdeadbeef.int64 + + const items = [ + (var1: test(), var2: 100'u32), + (var1: test(), var2: 192'u32) + ] diff --git a/tests/tuples/ttuples_various.nim b/tests/tuples/ttuples_various.nim new file mode 100644 index 000000000..36171d5d7 --- /dev/null +++ b/tests/tuples/ttuples_various.nim @@ -0,0 +1,136 @@ +discard """ +output: ''' +it's nil +@[1, 2, 3] +''' +""" + +import macros + + +block anontuples: + proc `^` (a, b: int): int = + result = 1 + for i in 1..b: result = result * a + + var m = (0, 5) + var n = (56, 3) + + m = (n[0] + m[1], m[1] ^ n[1]) + + doAssert m == (61, 125) + + # also test we can produce unary anon tuples in a macro: + macro mm(): untyped = + result = newTree(nnkTupleConstr, newLit(13)) + + proc nowTuple(): (int,) = + result = (0,) + + doAssert nowTuple() == (Field0: 0) + doAssert mm() == (Field0: 13) + + + +block unpack_asgn: + proc foobar(): (int, int) = (2, 4) + + # test within a proc: + proc pp(x: var int) = + var y: int + (y, x) = foobar() + + template pt(x) = + var y: int + (x, y) = foobar() + + # test within a generic: + proc pg[T](x, y: var T) = + pt(x) + + # test as a top level statement: + var x, y, a, b: int + # test for regression: + (x, y) = (1, 2) + (x, y) = fooBar() + + doAssert x == 2 + doAssert y == 4 + + pp(a) + doAssert a == 4 + + pg(a, b) + doAssert a == 2 + doAssert b == 0 + + + +block tuple_subscript: + proc`[]` (t: tuple, key: string): string = + for name, field in fieldPairs(t): + if name == key: + return $field + return "" + + proc`[]` [A,B](t: tuple, key: string, op: (proc(x: A): B)): B = + for name, field in fieldPairs(t): + when field is A: + if name == key: + return op(field) + + proc`[]=`[T](t: var tuple, key: string, val: T) = + for name, field in fieldPairs(t): + when field is T: + if name == key: + field = val + + var tt = (a: 1, b: "str1") + + # test built in operator + tt[0] = 5 + + doAssert tt[0] == 5 + doAssert `[]`(tt, 0) == 5 + + # test overloaded operator + tt["b"] = "str2" + doAssert tt["b"] == "str2" + doAssert `[]`(tt, "b") == "str2" + doAssert tt["b", proc(s: string): int = s.len] == 4 + + + +block tuple_with_seq: + template foo(s: string = "") = + if s.len == 0: + echo "it's nil" + else: + echo s + foo + + # bug #2632 + proc takeTup(x: tuple[s: string;x: seq[int]]) = + discard + takeTup(("foo", @[])) + + #proc foobar(): () = + proc f(xs: seq[int]) = + discard + + proc g(t: tuple[n:int, xs:seq[int]]) = + discard + + when isMainModule: + f(@[]) # OK + g((1,@[1])) # OK + g((0,@[])) # NG + + # bug #2630 + type T = tuple[a: seq[int], b: int] + var t: T = (@[1,2,3], 7) + + proc test(s: seq[int]): T = + echo s + (s, 7) + t = test(t.a) diff --git a/tests/tuples/tuint_tuple.nim b/tests/tuples/tuint_tuple.nim deleted file mode 100644 index 24bcead5e..000000000 --- a/tests/tuples/tuint_tuple.nim +++ /dev/null @@ -1,10 +0,0 @@ -# bug #1986 found by gdmoore - -proc test(): int64 = - return 0xdeadbeef.int64 - -const items = [ - (var1: test(), var2: 100'u32), - (var1: test(), var2: 192'u32) -] - diff --git a/tests/tuples/tunpack_asgn.nim b/tests/tuples/tunpack_asgn.nim deleted file mode 100644 index 1dc7ff074..000000000 --- a/tests/tuples/tunpack_asgn.nim +++ /dev/null @@ -1,34 +0,0 @@ -discard """ - output: '''2 4 -4 -2 0''' -""" - -proc foobar(): (int, int) = (2, 4) - -# test within a proc: -proc pp(x: var int) = - var y: int - (y, x) = foobar() - -template pt(x) = - var y: int - (x, y) = foobar() - -# test within a generic: -proc pg[T](x, y: var T) = - pt(x) - -# test as a top level statement: -var x, y, a, b: int -# test for regression: -(x, y) = (1, 2) -(x, y) = fooBar() - -echo x, " ", y - -pp(a) -echo a - -pg(a, b) -echo a, " ", b diff --git a/tests/tuples/tuple_subscript.nim b/tests/tuples/tuple_subscript.nim deleted file mode 100644 index 021793dc3..000000000 --- a/tests/tuples/tuple_subscript.nim +++ /dev/null @@ -1,40 +0,0 @@ -discard """ - output: '''5 -5 -str2 -str2 -4''' -""" - -proc`[]` (t: tuple, key: string): string = - for name, field in fieldPairs(t): - if name == key: - return $field - return "" - - -proc`[]` [A,B](t: tuple, key: string, op: (proc(x: A): B)): B = - for name, field in fieldPairs(t): - when field is A: - if name == key: - return op(field) - -proc`[]=`[T](t: var tuple, key: string, val: T) = - for name, field in fieldPairs(t): - when field is T: - if name == key: - field = val - -var tt = (a: 1, b: "str1") - -# test built in operator -tt[0] = 5 -echo tt[0] -echo `[]`(tt, 0) - - -# test overloaded operator -tt["b"] = "str2" -echo tt["b"] -echo `[]`(tt, "b") -echo tt["b", proc(s: string) : int = s.len] \ No newline at end of file diff --git a/tests/tuples/tuple_with_seq.nim b/tests/tuples/tuple_with_seq.nim deleted file mode 100644 index 00b07dd2c..000000000 --- a/tests/tuples/tuple_with_seq.nim +++ /dev/null @@ -1,46 +0,0 @@ -discard """ - output: '''it's nil -@[1, 2, 3]''' -""" - -template foo(s: string = "") = - if s.len == 0: - echo "it's nil" - else: - echo s - -foo - - -# bug #2632 - -proc takeTup(x: tuple[s: string;x: seq[int]]) = - discard - -takeTup(("foo", @[])) - - -#proc foobar(): () = - -proc f(xs: seq[int]) = - discard - -proc g(t: tuple[n:int, xs:seq[int]]) = - discard - -when isMainModule: - f(@[]) # OK - g((1,@[1])) # OK - g((0,@[])) # NG - - -# bug #2630 -type T = tuple[a: seq[int], b: int] - -var t: T = (@[1,2,3], 7) - -proc test(s: seq[int]): T = - echo s - (s, 7) - -t = test(t.a) diff --git a/tests/typerel/tnoargopenarray.nim b/tests/typerel/tnoargopenarray.nim index 20ebe5ecc..9e2b2fb86 100644 --- a/tests/typerel/tnoargopenarray.nim +++ b/tests/typerel/tnoargopenarray.nim @@ -1,7 +1,8 @@ +discard """ +action: compile +""" import db_sqlite var db: DbConn exec(db, sql"create table blabla()") - - diff --git a/tests/typerel/trettypeinference.nim b/tests/typerel/trettypeinference.nim index fa4e89cc8..aa0d66e5b 100644 --- a/tests/typerel/trettypeinference.nim +++ b/tests/typerel/trettypeinference.nim @@ -1,5 +1,5 @@ discard """ - msg: "instantiated for string\ninstantiated for int\ninstantiated for bool" + nimout: "instantiated for string\ninstantiated for int\ninstantiated for bool" output: "int\nseq[string]\nA\nB\n100\ntrue" """ diff --git a/tests/typerel/tsecondarrayproperty.nim b/tests/typerel/tsecondarrayproperty.nim index 3a6879b16..315ad06bf 100644 --- a/tests/typerel/tsecondarrayproperty.nim +++ b/tests/typerel/tsecondarrayproperty.nim @@ -1,3 +1,7 @@ +discard """ +output: "4" +""" + type TFoo = object @@ -25,4 +29,3 @@ echo f.second[1] #echo `second[]`(f,1) # this is the only way I could use it, but not what I expected - diff --git a/tests/typerel/ttuple1.nim b/tests/typerel/ttuple1.nim index d5c80800c..a03cc510e 100644 --- a/tests/typerel/ttuple1.nim +++ b/tests/typerel/ttuple1.nim @@ -1,3 +1,9 @@ +discard """ +output: ''' +M=1000, D=500, C=100, L=50, X=10, V=5, I=1 +''' +""" + const romanNumbers = [ ("M", 1000), ("D", 500), ("C", 100), ("L", 50), ("X", 10), ("V", 5), ("I", 1) ] @@ -12,5 +18,3 @@ for key, val in items(romanNumbers): proc PrintBiTuple(t: tuple[k: string, v: int]): int = stdout.write(t.k & "=" & $t.v & ", ") return 0 - - diff --git a/tests/types/taliasbugs.nim b/tests/types/taliasbugs.nim index bdb2a7a32..f1b35edf6 100644 --- a/tests/types/taliasbugs.nim +++ b/tests/types/taliasbugs.nim @@ -1,5 +1,5 @@ discard """ - msg: '''true + nimout: '''true true true true diff --git a/tests/types/tauto_canbe_void.nim b/tests/types/tauto_canbe_void.nim index b071c08e1..c43215d1e 100644 --- a/tests/types/tauto_canbe_void.nim +++ b/tests/types/tauto_canbe_void.nim @@ -1,3 +1,10 @@ +discard """ +output: ''' +arg +arg +''' +""" + import sugar @@ -6,4 +13,3 @@ template tempo(s) = tempo((s: string)->auto => echo(s)) tempo((s: string) => echo(s)) - diff --git a/tests/vm/tconsteval.nim b/tests/vm/tconsteval.nim index f4260495a..2e0fcb888 100644 --- a/tests/vm/tconsteval.nim +++ b/tests/vm/tconsteval.nim @@ -1,4 +1,5 @@ discard """ +action: compile """ import strutils @@ -28,4 +29,3 @@ Possible Commands: CompileDate, CompileTime] echo HelpText - diff --git a/tests/vm/tconsttable2.nim b/tests/vm/tconsttable2.nim index e07734eb5..5a392fb66 100644 --- a/tests/vm/tconsttable2.nim +++ b/tests/vm/tconsttable2.nim @@ -1,5 +1,5 @@ discard """ - msg: '''61''' + nimout: '''61''' """ # bug #2297 diff --git a/tests/vm/teval1.nim b/tests/vm/teval1.nim index 0eaa050da..5c323f0e7 100644 --- a/tests/vm/teval1.nim +++ b/tests/vm/teval1.nim @@ -1,3 +1,8 @@ + +discard """ +nimout: "##" +""" + import macros proc testProc: string {.compileTime.} = @@ -14,9 +19,9 @@ when true: const x = testProc() -echo "##", x, "##" +doAssert x == "" # bug #1310 static: - var i, j: set[int8] = {} - var k = i + j + var i, j: set[int8] = {} + var k = i + j diff --git a/tests/vm/tgorge.nim b/tests/vm/tgorge.nim index 694754f41..11c49a4cc 100644 --- a/tests/vm/tgorge.nim +++ b/tests/vm/tgorge.nim @@ -1,3 +1,10 @@ +discard """ +disabled: "windows" +""" + +# If your os is windows and this test fails for you locally, please +# check what is going wrong. + import os template getScriptDir(): string = diff --git a/tests/vm/tgorge.sh b/tests/vm/tgorge.sh index ba47afeae..ba47afeae 100644..100755 --- a/tests/vm/tgorge.sh +++ b/tests/vm/tgorge.sh diff --git a/tests/vm/tgorgeex.sh b/tests/vm/tgorgeex.sh index 36ba0a02f..36ba0a02f 100644..100755 --- a/tests/vm/tgorgeex.sh +++ b/tests/vm/tgorgeex.sh diff --git a/tests/vm/tinheritance.nim b/tests/vm/tinheritance.nim index d465e22b9..a94ccafcd 100644 --- a/tests/vm/tinheritance.nim +++ b/tests/vm/tinheritance.nim @@ -1,5 +1,5 @@ discard """ - msg: '''Hello fred , managed by sally + nimout: '''Hello fred , managed by sally Hello sally , managed by bob''' """ # bug #3973 @@ -27,3 +27,54 @@ proc test() = static: test() + +#---------------------------------------------- +# Bugs #9701 and #9702 +type + MyKind = enum + kA, kB, kC + + Base = ref object of RootObj + x: string + + A = ref object of Base + a: string + + B = ref object of Base + b: string + + C = ref object of B + c: string + +template check_templ(n: Base, k: MyKind) = + if k == kA: doAssert(n of A) else: doAssert(not (n of A)) + if k in {kB, kC}: doAssert(n of B) else: doAssert(not (n of B)) + if k == kC: doAssert(n of C) else: doAssert(not (n of C)) + doAssert(n of Base) + +proc check_proc(n: Base, k: MyKind) = + if k == kA: doAssert(n of A) else: doAssert(not (n of A)) + if k in {kB, kC}: doAssert(n of B) else: doAssert(not (n of B)) + if k == kC: doAssert(n of C) else: doAssert(not (n of C)) + doAssert(n of Base) + +static: + let aa = new(A) + check_templ(aa, kA) + check_proc(aa, kA) + let bb = new(B) + check_templ(bb, kB) + check_proc(bb, kB) + let cc = new(C) + check_templ(cc, kC) + check_proc(cc, kC) + +let aa = new(A) +check_templ(aa, kA) +check_proc(aa, kA) +let bb = new(B) +check_templ(bb, kB) +check_proc(bb, kB) +let cc = new(C) +check_templ(cc, kC) +check_proc(cc, kC) diff --git a/tests/vm/tmitems.nim b/tests/vm/tmitems.nim index a0e64d6aa..87835d1cd 100644 --- a/tests/vm/tmitems.nim +++ b/tests/vm/tmitems.nim @@ -1,5 +1,5 @@ discard """ - msg: '''13''' + nimout: '''13''' output: '''3 3 3''' diff --git a/tests/vm/tsimpleglobals.nim b/tests/vm/tsimpleglobals.nim index 27bfdce50..7ab665070 100644 --- a/tests/vm/tsimpleglobals.nim +++ b/tests/vm/tsimpleglobals.nim @@ -1,5 +1,5 @@ discard """ - msg: "abc xyz bb" + nimout: "abc xyz bb" """ # bug #2473 diff --git a/tests/vm/tslurp.nim b/tests/vm/tslurp.nim index d0041eaad..d762eb079 100644 --- a/tests/vm/tslurp.nim +++ b/tests/vm/tslurp.nim @@ -7,6 +7,6 @@ const relRes = slurp"./tslurp.nim" absRes = slurp(getScriptDir() / "tslurp.nim") -echo relRes -echo absRes - +doAssert relRes.len > 200 +doAssert absRes.len > 200 +doAssert relRes == absRes diff --git a/tests/vm/tstaticprintseq.nim b/tests/vm/tstaticprintseq.nim index 246a0211b..37bf62246 100644 --- a/tests/vm/tstaticprintseq.nim +++ b/tests/vm/tstaticprintseq.nim @@ -1,5 +1,5 @@ discard """ - msg: '''1 + nimout: '''1 2 3 1 diff --git a/tests/vm/tswap.nim b/tests/vm/tswap.nim index 2219be9ca..4243b5a71 100644 --- a/tests/vm/tswap.nim +++ b/tests/vm/tswap.nim @@ -1,5 +1,5 @@ discard """ -msg: ''' +nimout: ''' x.data = @[10] y = @[11] x.data = @[11] diff --git a/tests/vm/ttouintconv.nim b/tests/vm/ttouintconv.nim index 5f8884e80..dd4c597ba 100644 --- a/tests/vm/ttouintconv.nim +++ b/tests/vm/ttouintconv.nim @@ -1,7 +1,7 @@ import macros discard """ -msg: ''' +nimout: ''' 8 9 17 239 255 61439 65534 65535 diff --git a/tests/vm/tvmmisc.nim b/tests/vm/tvmmisc.nim index 350f3c08e..85de26e39 100644 --- a/tests/vm/tvmmisc.nim +++ b/tests/vm/tvmmisc.nim @@ -1,7 +1,6 @@ # bug #4462 import macros import os -import ospaths import strutils block: diff --git a/tools/kochdocs.nim b/tools/kochdocs.nim index c54f820fe..7cd13b2ac 100644 --- a/tools/kochdocs.nim +++ b/tools/kochdocs.nim @@ -52,14 +52,25 @@ proc execCleanPath*(cmd: string, putEnv("PATH", prevPath) proc nimexec*(cmd: string) = + # Consider using `nimCompile` instead exec findNim() & " " & cmd +proc nimCompile*(input: string, outputDir = "bin", mode = "c", options = "") = + # TODO: simplify pending https://github.com/nim-lang/Nim/issues/9513 + var cmd = findNim() & " " & mode + let output = outputDir / input.splitFile.name.exe + cmd.add " -o:" & output + cmd.add " " & options + cmd.add " " & input + exec cmd + const pdf = """ doc/manual.rst doc/lib.rst doc/tut1.rst doc/tut2.rst +doc/tut3.rst doc/nimc.rst doc/niminst.rst doc/gc.rst @@ -72,6 +83,7 @@ doc/lib.rst doc/manual.rst doc/tut1.rst doc/tut2.rst +doc/tut3.rst doc/nimc.rst doc/overview.rst doc/filters.rst @@ -95,7 +107,7 @@ doc/manual/var_t_return.rst doc = """ lib/system.nim lib/system/nimscript.nim -lib/pure/ospaths.nim +lib/deprecated/pure/ospaths.nim lib/pure/parsejson.nim lib/pure/cstrutils.nim lib/core/macros.nim @@ -115,7 +127,8 @@ lib/pure/os.nim lib/pure/strutils.nim lib/pure/math.nim lib/pure/matchers.nim -lib/pure/editdistance.nim +lib/std/editdistance.nim +lib/std/wordwrap.nim lib/pure/algorithm.nim lib/pure/stats.nim lib/windows/winlean.nim diff --git a/tools/nimfind.nim b/tools/nimfind.nim new file mode 100644 index 000000000..b9c7d8ac9 --- /dev/null +++ b/tools/nimfind.nim @@ -0,0 +1,237 @@ +# +# +# The Nim Compiler +# (c) Copyright 2018 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## Nimfind is a tool that helps to give editors IDE like capabilities. + +when not defined(nimcore): + {.error: "nimcore MUST be defined for Nim's core tooling".} +when not defined(nimfind): + {.error: "nimfind MUST be defined for Nim's nimfind tool".} + +const Usage = """ +Nimfind - Tool to find declarations or usages for Nim symbols +Usage: + nimfind [options] file.nim:line:col + +Options: + --help, -h show this help + --rebuild rebuild the index + --project:file.nim use file.nim as the entry point + +In addition, all command line options of Nim that do not affect code generation +are supported. +""" + +import strutils, os, parseopt, parseutils + +import "../compiler" / [options, commands, modules, sem, + passes, passaux, msgs, nimconf, + extccomp, condsyms, + ast, scriptconfig, + idents, modulegraphs, vm, prefixmatches, lineinfos, cmdlinehelper, + pathutils] + +import db_sqlite + +proc createDb(db: DbConn) = + db.exec(sql""" + create table if not exists filenames( + id integer primary key, + fullpath varchar(8000) not null + ); + """) + db.exec sql"create index if not exists FilenameIx on filenames(fullpath);" + + # every sym can have potentially 2 different definitions due to forward + # declarations. + db.exec(sql""" + create table if not exists syms( + id integer primary key, + nimid integer not null, + name varchar(256) not null, + defline integer not null, + defcol integer not null, + deffile integer not null, + deflineB integer not null default 0, + defcolB integer not null default 0, + deffileB integer not null default 0, + foreign key (deffile) references filenames(id), + foreign key (deffileB) references filenames(id) + ); + """) + + db.exec(sql""" + create table if not exists usages( + id integer primary key, + nimid integer not null, + line integer not null, + col integer not null, + colB integer not null, + file integer not null, + foreign key (file) references filenames(id), + foreign key (nimid) references syms(nimid) + ); + """) + db.exec sql"create index if not exists UsagesIx on usages(file, line);" + +proc toDbFileId*(db: DbConn; conf: ConfigRef; fileIdx: FileIndex): int = + if fileIdx == FileIndex(-1): return -1 + let fullpath = toFullPath(conf, fileIdx) + let row = db.getRow(sql"select id from filenames where fullpath = ?", fullpath) + let id = row[0] + if id.len == 0: + result = int db.insertID(sql"insert into filenames(fullpath) values (?)", + fullpath) + else: + result = parseInt(id) + +type + FinderRef = ref object of RootObj + db: DbConn + +proc writeDef(graph: ModuleGraph; s: PSym; info: TLineInfo) = + let f = FinderRef(graph.backend) + f.db.exec(sql"""insert into syms(nimid, name, defline, defcol, deffile) values (?, ?, ?, ?, ?)""", + s.id, s.name.s, info.line, info.col, + toDbFileId(f.db, graph.config, info.fileIndex)) + +proc writeDefResolveForward(graph: ModuleGraph; s: PSym; info: TLineInfo) = + let f = FinderRef(graph.backend) + f.db.exec(sql"""update syms set deflineB = ?, defcolB = ?, deffileB = ? + where nimid = ?""", info.line, info.col, + toDbFileId(f.db, graph.config, info.fileIndex), s.id) + +proc writeUsage(graph: ModuleGraph; s: PSym; info: TLineInfo) = + let f = FinderRef(graph.backend) + f.db.exec(sql"""insert into usages(nimid, line, col, colB, file) values (?, ?, ?, ?, ?)""", + s.id, info.line, info.col, info.col + s.name.s.len - 1, + toDbFileId(f.db, graph.config, info.fileIndex)) + +proc performSearch(conf: ConfigRef; dbfile: AbsoluteFile) = + var db = open(connection=string dbfile, user="nim", password="", + database="nim") + let pos = conf.m.trackPos + let fid = toDbFileId(db, conf, pos.fileIndex) + let known = toFullPath(conf, pos.fileIndex) + let nimids = db.getRow(sql"""select distinct nimid from usages where line = ? and file = ? and ? between col and colB""", + pos.line, fid, pos.col) + if nimids.len > 0: + var idSet = "" + for id in nimids: + if idSet.len > 0: idSet.add ", " + idSet.add id + var outputLater = "" + for r in db.rows(sql"""select line, col, filenames.fullpath from usages + inner join filenames on filenames.id = file + where nimid in (?)""", idSet): + let line = parseInt(r[0]) + let col = parseInt(r[1]) + let file = r[2] + if file == known and line == pos.line.int: + # output the line we already know last: + outputLater.add file & ":" & $line & ":" & $(col+1) & "\n" + else: + echo file, ":", line, ":", col+1 + if outputLater.len > 0: stdout.write outputLater + close(db) + +proc setupDb(g: ModuleGraph; dbfile: AbsoluteFile) = + var f = FinderRef() + removeFile(dbfile) + f.db = open(connection=string dbfile, user="nim", password="", + database="nim") + createDb(f.db) + f.db.exec(sql"pragma journal_mode=off") + # This MUST be turned off, otherwise it's way too slow even for testing purposes: + f.db.exec(sql"pragma SYNCHRONOUS=off") + f.db.exec(sql"pragma LOCKING_MODE=exclusive") + g.backend = f + +proc mainCommand(graph: ModuleGraph) = + let conf = graph.config + let dbfile = getNimcacheDir(conf) / RelativeFile"nimfind.db" + if not fileExists(dbfile) or optForceFullMake in conf.globalOptions: + clearPasses(graph) + registerPass graph, verbosePass + registerPass graph, semPass + conf.cmd = cmdIdeTools + wantMainModule(conf) + setupDb(graph, dbfile) + + graph.onDefinition = writeUsage # writeDef + graph.onDefinitionResolveForward = writeUsage # writeDefResolveForward + graph.onUsage = writeUsage + + if not fileExists(conf.projectFull): + quit "cannot find file: " & conf.projectFull.string + add(conf.searchPaths, conf.libpath) + # do not stop after the first error: + conf.errorMax = high(int) + try: + compileProject(graph) + finally: + close(FinderRef(graph.backend).db) + performSearch(conf, dbfile) + +proc processCmdLine*(pass: TCmdLinePass, cmd: string; conf: ConfigRef) = + var p = parseopt.initOptParser(cmd) + while true: + parseopt.next(p) + case p.kind + of cmdEnd: break + of cmdLongoption, cmdShortOption: + case p.key.normalize + of "help", "h": + stdout.writeline(Usage) + quit() + of "project": + conf.projectName = p.val + of "rebuild": + incl conf.globalOptions, optForceFullMake + else: processSwitch(pass, p, conf) + of cmdArgument: + let info = p.key.split(':') + if info.len == 3: + let (dir, file, ext) = info[0].splitFile() + conf.projectName = findProjectNimFile(conf, dir) + if conf.projectName.len == 0: conf.projectName = info[0] + try: + conf.m.trackPos = newLineInfo(conf, AbsoluteFile info[0], + parseInt(info[1]), parseInt(info[2])-1) + except ValueError: + quit "invalid command line" + else: + quit "invalid command line" + +proc handleCmdLine(cache: IdentCache; conf: ConfigRef) = + let self = NimProg( + suggestMode: true, + processCmdLine: processCmdLine, + mainCommand: mainCommand + ) + self.initDefinesProg(conf, "nimfind") + + if paramCount() == 0: + stdout.writeline(Usage) + return + + self.processCmdLineAndProjectPath(conf) + + # Find Nim's prefix dir. + let binaryPath = findExe("nim") + if binaryPath == "": + raise newException(IOError, + "Cannot find Nim standard library: Nim compiler not in PATH") + conf.prefixDir = AbsoluteDir binaryPath.splitPath().head.parentDir() + if not dirExists(conf.prefixDir / RelativeDir"lib"): + conf.prefixDir = AbsoluteDir"" + + discard self.loadConfigsAndRunMainCommand(cache, conf) + +handleCmdline(newIdentCache(), newConfigRef()) diff --git a/tools/nimfind.nims b/tools/nimfind.nims new file mode 100644 index 000000000..3013c360d --- /dev/null +++ b/tools/nimfind.nims @@ -0,0 +1,2 @@ +--define:nimfind +--define:nimcore diff --git a/tools/niminst/buildbat.tmpl b/tools/niminst/buildbat.nimf index 6767461e3..6767461e3 100644 --- a/tools/niminst/buildbat.tmpl +++ b/tools/niminst/buildbat.nimf diff --git a/tools/niminst/buildsh.tmpl b/tools/niminst/buildsh.nimf index acd58bda2..04ef35653 100644 --- a/tools/niminst/buildsh.tmpl +++ b/tools/niminst/buildsh.nimf @@ -1,7 +1,7 @@ #? stdtmpl(subsChar='?') | standard #proc generateBuildShellScript(c: ConfigData): string = # result = "#! /bin/sh\n# Generated from niminst\n" & -# "# Template is in tools/niminst/buildsh.tmpl\n" & +# "# Template is in tools/niminst/buildsh.nimf\n" & # "# To regenerate run ``niminst csource`` or ``koch csource``\n" set -e diff --git a/tools/niminst/debcreation.nim b/tools/niminst/debcreation.nim index 0ecea132f..60aa48639 100644 --- a/tools/niminst/debcreation.nim +++ b/tools/niminst/debcreation.nim @@ -84,7 +84,7 @@ proc createCopyright(pkgName, mtnName, mtnEmail, version: string, addN("Files: " & f) addN("License: " & license) -proc formatDateTime(t: TimeInfo, timezone: string): string = +proc formatDateTime(t: DateTime, timezone: string): string = var day = ($t.weekday)[0..2] & ", " return "$1$2 $3 $4 $5:$6:$7 $8" % [day, intToStr(t.monthday, 2), diff --git a/tools/niminst/deinstall.tmpl b/tools/niminst/deinstall.nimf index 8b4477369..8b4477369 100644 --- a/tools/niminst/deinstall.tmpl +++ b/tools/niminst/deinstall.nimf diff --git a/tools/niminst/inno.tmpl b/tools/niminst/inno.nimf index ef2da8a75..ef2da8a75 100644 --- a/tools/niminst/inno.tmpl +++ b/tools/niminst/inno.nimf diff --git a/tools/niminst/install.tmpl b/tools/niminst/install.nimf index a78914ecd..a78914ecd 100644 --- a/tools/niminst/install.tmpl +++ b/tools/niminst/install.nimf diff --git a/tools/niminst/makefile.tmpl b/tools/niminst/makefile.nimf index aadd0e94d..3467f025e 100644 --- a/tools/niminst/makefile.tmpl +++ b/tools/niminst/makefile.nimf @@ -1,7 +1,7 @@ #? stdtmpl(subsChar='?') | standard #proc generateMakefile(c: ConfigData): string = # result = "# Generated from niminst\n" & -# "# Template is in tools/niminst/makefile.tmpl\n" & +# "# Template is in tools/niminst/makefile.nimf\n" & # "# To regenerate run ``niminst csource`` or ``koch csource``\n" CC ??= gcc diff --git a/tools/niminst/niminst.nim b/tools/niminst/niminst.nim index 3c5572a07..9e428993a 100644 --- a/tools/niminst/niminst.nim +++ b/tools/niminst/niminst.nim @@ -126,13 +126,13 @@ proc skipRoot(f: string): string = inc i if result.len == 0: result = f -include "inno.tmpl" -include "nsis.tmpl" -include "buildsh.tmpl" -include "makefile.tmpl" -include "buildbat.tmpl" -include "install.tmpl" -include "deinstall.tmpl" +include "inno.nimf" +include "nsis.nimf" +include "buildsh.nimf" +include "makefile.nimf" +include "buildbat.nimf" +include "install.nimf" +include "deinstall.nimf" # ------------------------- configuration file ------------------------------- diff --git a/tools/niminst/nsis.tmpl b/tools/niminst/nsis.nimf index f4eb1d0cd..f4eb1d0cd 100644 --- a/tools/niminst/nsis.tmpl +++ b/tools/niminst/nsis.nimf diff --git a/tools/nimweb.nim b/tools/nimweb.nim index b7fee220a..460135f49 100644 --- a/tools/nimweb.nim +++ b/tools/nimweb.nim @@ -71,7 +71,7 @@ proc initConfigData(c: var TConfigData) = c.gitCommit = output.strip c.quotations = initTable[string, tuple[quote, author: string]]() -include "website.tmpl" +include "website.nimf" # ------------------------- configuration file ------------------------------- diff --git a/tools/vccenv/vccenv.nim b/tools/vccenv/vccenv.nim index a5af7994e..1f172c7c0 100644 --- a/tools/vccenv/vccenv.nim +++ b/tools/vccenv/vccenv.nim @@ -3,6 +3,7 @@ import strtabs, os, osproc, streams, strutils const comSpecEnvKey = "ComSpec" vsComnToolsEnvKeys = [ + "VS150COMNTOOLS", "VS140COMNTOOLS", "VS130COMNTOOLS", "VS120COMNTOOLS", diff --git a/tools/website.tmpl b/tools/website.nimf index 9e5eb2460..9e5eb2460 100644 --- a/tools/website.tmpl +++ b/tools/website.nimf |