diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000000..06dd755365 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,12 @@ +# These are supported funding model platforms + +github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] +patreon: # Replace with a single Patreon username +open_collective: 'nim' # Replace with a single Open Collective username +ko_fi: # Replace with a single Ko-fi username +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +otechie: # Replace with a single Otechie username +custom: 'https://nim-lang.org/donate.html' # Replace with a single custom sponsorship URL diff --git a/changelog.md b/changelog.md index 9ddfd36dcc..87032fc0e3 100644 --- a/changelog.md +++ b/changelog.md @@ -1,294 +1,33 @@ -## v0.20.0 - 2019-06-06 +## v0.20.2 - XXXX-XX-XX ### Changes affecting backwards compatibility -- `shr` is now sign preserving. Use `-d:nimOldShiftRight` to enable - the old behavior globally. - -- The ``isLower``, ``isUpper`` family of procs in strutils/unicode - operating on **strings** have been - deprecated since it was unclear what these do. Note that the much more - useful procs that operate on ``char`` or ``Rune`` are not affected. - -- `strutils.editDistance` has been deprecated, - use `editdistance.editDistance` or `editdistance.editDistanceAscii` - instead. - -- The OpenMP parallel iterator \``||`\` now supports any `#pragma omp directive` - 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`. - - Furthermore, an overload with positive stepping is available. - -- 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. - -- `getImpl` on a `var` or `let` symbol will now return the full `IdentDefs` - tree from the symbol declaration instead of just the initializer portion. - -- Methods are now ordinary "single" methods, only the first parameter is - used to select the variant at runtime. For backwards compatibility - use the new `--multimethods:on` switch. - -- Generic methods are now deprecated; they never worked well. - -- Compile time checks for integer and float conversions are now stricter. - For example, `const x = uint32(-1)` now gives a compile time error instead - of being equivalent to `const x = 0xFFFFFFFF'u32`. - -- Using `typed` as the result type in templates/macros now means - "expression with a type". The old meaning of `typed` is preserved - as `void` or no result type at all. - -- A bug allowed `macro foo(): int = 123` to compile even though a - macro has to return a `NimNode`. This has been fixed. - -- With the exception of `uint` and `uint64`, conversion to unsigned types - are now range checked during runtime. - -- Macro arguments of type `typedesc` are now passed to the macro as - `NimNode` like every other type except `static`. Use `typed` for a - behavior that is identical in new and old - Nim. See the RFC [Pass typedesc as NimNode to macros](https://github.com/nim-lang/RFCs/issues/148) - for more details. - #### Breaking changes in the standard library -- `osproc.execProcess` now also takes a `workingDir` parameter. - -- `std/sha1.secureHash` now accepts `openArray[char]`, not `string`. (Former - successful matches should keep working, though former failures will not.) - -- `options.UnpackError` is no longer a ref type and inherits from `system.Defect` - instead of `system.ValueError`. - -- `system.ValueError` now inherits from `system.CatchableError` instead of `system.Defect`. - -- The procs `parseutils.parseBiggestInt`, `parseutils.parseInt`, - `parseutils.parseBiggestUInt` and `parseutils.parseUInt` now raise a - `ValueError` when the parsed integer is outside of the valid range. - Previously they sometimes raised an `OverflowError` and sometimes they - returned `0`. - -- The procs `parseutils.parseBin`, `parseutils.parseOct` and `parseutils.parseHex` - were not clearing their `var` parameter `number` and used to push its value to - the left when storing the parsed string into it. Now they always set the value - of the parameter to `0` before storing the result of the parsing, unless the - string to parse is not valid (then the value of `number` is not changed). - -- `streams.StreamObject` now restricts its fields to only raise `system.Defect`, - `system.IOError` and `system.OSError`. - This change only affects custom stream implementations. - -- nre's `RegexMatch.{captureBounds,captures}[]` no longer return `Option` or - `nil`/`""`, respectively. Use the newly added `n in p.captures` method to - check if a group is captured, otherwise you'll receive an exception. - -- nre's `RegexMatch.{captureBounds,captures}.toTable` no longer accept a - default parameter. Instead uncaptured entries are left empty. Use - `Table.getOrDefault()` if you need defaults. - -- nre's `RegexMatch.captures.{items,toSeq}` now returns an `Option[string]` - instead of a `string`. With the removal of `nil` strings, this is the only - way to indicate a missing match. Inside your loops, instead - of `capture == ""` or `capture == nil`, use `capture.isSome` to check if a capture is - present, and `capture.get` to get its value. - -- nre's `replace()` no longer throws `ValueError` when the replacement string - has missing captures. It instead throws `KeyError` for named captures, and - `IndexError` for unnamed captures. This is consistent with - `RegexMatch.{captureBounds,captures}[]`. - -- `splitFile` now correctly handles edge cases, see #10047. - -- `isNil` is no longer false for undefined in the JavaScript backend: - now it's true for both nil and undefined. - Use `isNull` or `isUndefined` if you need exact equality: - `isNil` is consistent with `===`, `isNull` and `isUndefined` with `==`. - -- several deprecated modules were removed: `ssl`, `matchers`, `httpserver`, - `unsigned`, `actors`, `parseurl` - -- two poorly documented and not used modules (`subexes`, `scgi`) were moved to - graveyard (they are available as Nimble packages) - -- procs `string.add(int)` and `string.add(float)` which implicitly convert - ints and floats to string have been deprecated. - Use `string.addInt(int)` and `string.addFloat(float)` instead. - -- ``case object`` branch transitions via ``system.reset`` are deprecated. - Compile your code with ``-d:nimOldCaseObjects`` for a transition period. - -- base64 module: The default parameter `newLine` for the `encode` procs - was changed from `"\13\10"` to the empty string `""`. - #### Breaking changes in the compiler -- The compiler now implements the "generic symbol prepass" for `when` statements - in generics, see bug #8603. This means that code like this does not compile - anymore: - -```nim -proc enumToString*(enums: openArray[enum]): string = - # typo: 'e' instead 'enums' - when e.low.ord >= 0 and e.high.ord < 256: - result = newString(enums.len) - else: - result = newString(enums.len * 2) -``` - -- ``discard x`` is now illegal when `x` is a function symbol. - -- Implicit imports via ``--import: module`` in a config file are now restricted - to the main package. - ### Library additions -- 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`. - -- Added `or` for `NimNode` in `macros`. - -- 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. - -- Added the parameter ``isSorted`` for the ``sequtils.deduplicate`` proc. - -- There is a new stdlib module `std/diff` to compute the famous "diff" - of two texts by line. - -- Added `os.relativePath`. - -- Added `parseopt.remainingArgs`. - -- Added `os.getCurrentCompilerExe` (implemented as `getAppFilename` at CT), - can be used to retrieve the currently executing compiler. - -- Added `xmltree.toXmlAttributes`. - -- Added ``std/sums`` module for fast summation functions. - -- Added `Rusage`, `getrusage`, `wait4` to the posix interface. - -- Added the `posix_utils` module. - -- Added `system.default`. - -- Added `sequtils.items` for closure iterators, allows closure iterators - to be used by the `mapIt`, `filterIt`, `allIt`, `anyIt`, etc. - ### 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. - -- Added `macros.signatureHash` that returns a stable identifier - derived from the signature of a symbol. - -- In `strutils` empty strings now no longer matched as substrings - anymore. - -- The `Complex` type is now a generic object 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.) - -- `os.joinPath` and `os.normalizePath` handle edge cases like ``"a/b/../../.."`` - differently. - -- `securehash` was moved to `lib/deprecated`. - -- The switch ``-d:useWinAnsi`` is not supported anymore. - -- In `times` module, procs `format` and `parse` accept a new optional - `DateTimeLocale` argument for formatting/parsing dates in other languages. ### Language additions -- Vm support for float32<->int32 and float64<->int64 casts was added. -- There is a new pragma block `noSideEffect` that works like - the `gcsafe` pragma block. -- added `os.getCurrentProcessId`. -- User defined pragmas are now allowed in the pragma blocks. -- Pragma blocks are no longer eliminated from the typed AST tree to preserve - pragmas for further analysis by macros. -- Custom pragmas are now supported for `var` and `let` symbols. -- Tuple unpacking is now supported for constants and for loop variables. -- Case object branches can be initialized with a runtime discriminator if - possible discriminator values are constrained within a case statement. - ### 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. - -- Pragma syntax is now consistent. Previous syntax where type pragmas did not - follow the type name is now deprecated. Also pragma before generic parameter - list is deprecated to be consistent with how pragmas are used with a proc. See - [#8514](https://github.com/nim-lang/Nim/issues/8514) and - [#1872](https://github.com/nim-lang/Nim/issues/1872) for further details. - -- Hash sets and tables are initialized by default. The explicit `initHashSet`, - `initTable`, etc. are not needed anymore. - ### Tool changes -- `jsondoc` now includes a `moduleDescription` field with the module - description. `jsondoc0` shows comments as its own objects as shown in the - documentation. -- `nimpretty`: --backup now defaults to `off` instead of `on` and the flag was - undocumented; use `git` instead of relying on backup files. -- `koch` now defaults to build the latest *stable* Nimble version unless you - explicitly ask for the latest master version via `--latest`. ### Compiler changes -- The deprecated `fmod` proc is now unavailable on the VM. -- A new `--outdir` option was added. -- The compiled JavaScript file for the project produced by executing `nim js` - will no longer be placed in the nimcache directory. -- The `--hotCodeReloading` has been implemented for the native targets. - The compiler also provides a new more flexible API for handling the - hot code reloading events in the code. -- The compiler now supports a ``--expandMacro:macroNameHere`` switch - for easy introspection into what a macro expands into. -- The `-d:release` switch now does not disable runtime checks anymore. - For a release build that also disables runtime checks - use `-d:release -d:danger` or simply `-d:danger`. ### Bugfixes diff --git a/compiler/layouter.nim b/compiler/layouter.nim index 6cb129fc1e..3ac907f1d1 100644 --- a/compiler/layouter.nim +++ b/compiler/layouter.nim @@ -12,10 +12,10 @@ import idents, lexer, lineinfos, llstream, options, msgs, strutils, pathutils from os import changeFileExt +from sequtils import delete const MaxLineLen = 80 - LineCommentColumn = 30 type SplitKind = enum @@ -24,6 +24,12 @@ type SemicolonKind = enum detectSemicolonKind, useSemicolon, dontTouch + LayoutToken = enum + ltSpaces, ltNewline, ltTab, + ltComment, ltLit, ltKeyword, ltExportMarker, ltIdent, + ltOther, ltOpr, + ltBeginSection, ltEndSection + Emitter* = object config: ConfigRef fid: FileIndex @@ -33,7 +39,8 @@ type col, lastLineNumber, lineSpan, indentLevel, indWidth*: int keepIndents*: int doIndentMore*: int - content: string + kinds: seq[LayoutToken] + tokens: seq[string] indentStack: seq[int] fixedUntil: int # marks where we must not go in the content altSplitPos: array[SplitKind, int] # alternative split positions @@ -50,21 +57,72 @@ proc openEmitter*(em: var Emitter, cache: IdentCache; em.lastTok = tkInvalid em.inquote = false em.col = 0 - em.content = newStringOfCap(16_000) em.indentStack = newSeqOfCap[int](30) em.indentStack.add 0 em.lastLineNumber = 1 +proc computeMax(em: Emitter; pos: int): int = + var p = pos + result = 0 + while p < em.tokens.len and em.kinds[p] != ltEndSection: + var lhs = 0 + var lineLen = 0 + var foundTab = false + while p < em.tokens.len and em.kinds[p] != ltEndSection: + if em.kinds[p] == ltNewline: + if foundTab and lineLen <= MaxLineLen: result = max(result, lhs) + inc p + break + if em.kinds[p] == ltTab: + foundTab = true + else: + if not foundTab: + inc lhs, em.tokens[p].len + inc lineLen, em.tokens[p].len + inc p + +proc computeRhs(em: Emitter; pos: int): int = + var p = pos + result = 0 + while p < em.tokens.len and em.kinds[p] != ltNewline: + inc result, em.tokens[p].len + inc p + proc closeEmitter*(em: var Emitter) = let outFile = em.config.absOutFile - if fileExists(outFile) and readFile(outFile.string) == em.content: + + var content = newStringOfCap(16_000) + var maxLhs = 0 + var lineLen = 0 + var lineBegin = 0 + for i in 0..em.tokens.high: + case em.kinds[i] + of ltBeginSection: + maxLhs = computeMax(em, lineBegin) + of ltEndSection: + maxLhs = 0 + of ltTab: + if maxLhs == 0 or computeRhs(em, i)+maxLhs > MaxLineLen: + content.add ' ' + else: + let spaces = max(maxLhs - lineLen + 1, 1) + for j in 1..spaces: content.add ' ' + of ltNewline: + content.add em.tokens[i] + lineLen = 0 + lineBegin = i+1 + else: + content.add em.tokens[i] + inc lineLen, em.tokens[i].len + + if fileExists(outFile) and readFile(outFile.string) == content: discard "do nothing, see #9499" return var f = llStreamOpen(outFile, fmWrite) if f == nil: rawMessage(em.config, errGenerated, "cannot open file: " & outFile.string) return - f.llStreamWrite em.content + f.llStreamWrite content llStreamClose(f) proc countNewlines(s: string): int = @@ -79,9 +137,36 @@ proc calcCol(em: var Emitter; s: string) = dec i inc em.col -template wr(x) = - em.content.add x +proc wr(em: var Emitter; x: string; lt: LayoutToken) = + em.tokens.add x + em.kinds.add lt inc em.col, x.len + assert em.tokens.len == em.kinds.len + +proc wrNewline(em: var Emitter) = + em.tokens.add "\L" + em.kinds.add ltNewline + em.col = 0 + +proc wrSpaces(em: var Emitter; spaces: int) = + if spaces > 0: + wr(em, strutils.repeat(' ', spaces), ltSpaces) + +proc wrSpace(em: var Emitter) = + wr(em, " ", ltSpaces) + +proc wrTab(em: var Emitter) = + wr(em, " ", ltTab) + +proc beginSection*(em: var Emitter) = wr(em, "", ltBeginSection) +proc endSection*(em: var Emitter) = wr(em, "", ltEndSection) + +proc removeSpaces(em: var Emitter) = + while em.kinds.len > 0 and em.kinds[^1] == ltSpaces: + let tokenLen = em.tokens[^1].len + setLen(em.tokens, em.tokens.len-1) + setLen(em.kinds, em.kinds.len-1) + dec em.col, tokenLen template goodCol(col): bool = col in 40..MaxLineLen @@ -89,13 +174,17 @@ const openPars = {tkParLe, tkParDotLe, tkBracketLe, tkBracketLeColon, tkCurlyDotLe, tkCurlyLe} + closedPars = {tkParRi, tkParDotRi, + tkBracketRi, tkCurlyDotRi, + tkCurlyRi} + splitters = openPars + {tkComma, tkSemicolon} oprSet = {tkOpr, tkDiv, tkMod, tkShl, tkShr, tkIn, tkNotin, tkIs, tkIsnot, tkNot, tkOf, tkAs, tkDotDot, tkAnd, tkOr, tkXor} template rememberSplit(kind) = if goodCol(em.col): - em.altSplitPos[kind] = em.content.len + em.altSplitPos[kind] = em.tokens.len template moreIndent(em): int = (if em.doIndentMore > 0: em.indWidth*2 else: em.indWidth) @@ -106,54 +195,109 @@ proc softLinebreak(em: var Emitter, lit: string) = # +2 because we blindly assume a comma or ' &' might follow if not em.inquote and em.col+lit.len+2 >= MaxLineLen: if em.lastTok in splitters: - while em.content.len > 0 and em.content[em.content.high] == ' ': - setLen(em.content, em.content.len-1) - wr("\L") - em.col = 0 - for i in 1..em.indentLevel+moreIndent(em): wr(" ") + # bug #10295, check first if even more indentation would help: + let spaces = em.indentLevel+moreIndent(em) + if spaces < em.col: + removeSpaces em + wrNewline(em) + em.col = 0 + wrSpaces em, spaces else: # search backwards for a good split position: for a in mitems(em.altSplitPos): if a > em.fixedUntil: var spaces = 0 - while a+spaces < em.content.len and em.content[a+spaces] == ' ': + while a+spaces < em.kinds.len and em.kinds[a+spaces] == ltSpaces: inc spaces - if spaces > 0: delete(em.content, a, a+spaces-1) - em.col = em.content.len - a - let ws = "\L" & repeat(' ', em.indentLevel+moreIndent(em)) - em.content.insert(ws, a) + if spaces > 0: + delete(em.tokens, a, a+spaces-1) + delete(em.kinds, a, a+spaces-1) + em.kinds.insert(ltNewline, a) + em.tokens.insert("\L", a) + em.kinds.insert(ltSpaces, a+1) + em.tokens.insert(repeat(' ', em.indentLevel+moreIndent(em)), a+1) + # recompute em.col: + var i = em.kinds.len-1 + em.col = 0 + while i >= 0 and em.kinds[i] != ltNewline: + inc em.col, em.tokens[i].len + dec i + # mark position as "already split here" a = -1 break -proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) = +proc emitMultilineComment(em: var Emitter, lit: string, col: int) = + # re-align every line in the multi-line comment: + var i = 0 + var lastIndent = em.indentStack[^1] + var b = 0 + for commentLine in splitLines(lit): + let stripped = commentLine.strip() + var a = 0 + while a < commentLine.len and commentLine[a] == ' ': inc a + if i == 0: + discard + elif stripped.len == 0: + wrNewline em + else: + if a > lastIndent: + b += em.indWidth + lastIndent = a + elif a < lastIndent: + b -= em.indWidth + lastIndent = a + wrNewline em + wrSpaces em, col + b + wr em, stripped, ltComment + inc i - template endsInWhite(em): bool = - em.content.len == 0 or em.content[em.content.high] in {' ', '\L'} - template endsInAlpha(em): bool = - em.content.len > 0 and em.content[em.content.high] in SymChars+{'_'} +proc lastChar(s: string): char = + result = if s.len > 0: s[s.high] else: '\0' - proc emitComment(em: var Emitter; tok: TToken) = - let lit = strip fileSection(em.config, em.fid, tok.commentOffsetA, tok.commentOffsetB) - em.lineSpan = countNewlines(lit) - if em.lineSpan > 0: calcCol(em, lit) +proc endsInWhite(em: Emitter): bool = + var i = em.tokens.len-1 + while i >= 0 and em.kinds[i] in {ltBeginSection, ltEndSection}: dec(i) + result = if i >= 0: em.kinds[i] in {ltSpaces, ltNewline, ltTab} else: true + +proc endsInNewline(em: Emitter): bool = + var i = em.tokens.len-1 + while i >= 0 and em.kinds[i] in {ltBeginSection, ltEndSection, ltSpaces}: dec(i) + result = if i >= 0: em.kinds[i] in {ltNewline, ltTab} else: true + +proc endsInAlpha(em: Emitter): bool = + var i = em.tokens.len-1 + while i >= 0 and em.kinds[i] in {ltBeginSection, ltEndSection}: dec(i) + result = if i >= 0: em.tokens[i].lastChar in SymChars+{'_'} else: false + +proc emitComment(em: var Emitter; tok: TToken) = + let col = em.col + let lit = strip fileSection(em.config, em.fid, tok.commentOffsetA, tok.commentOffsetB) + em.lineSpan = countNewlines(lit) + if em.lineSpan > 0: calcCol(em, lit) + if em.lineSpan == 0: + if not endsInNewline(em): + wrTab em + wr em, lit, ltComment + else: if not endsInWhite(em): - wr(" ") - if em.lineSpan == 0 and max(em.col, LineCommentColumn) + lit.len <= MaxLineLen: - for i in 1 .. LineCommentColumn - em.col: wr(" ") - wr lit + wrTab em + emitMultilineComment(em, lit, col) + +proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) = + template wasExportMarker(em): bool = + em.kinds.len > 0 and em.kinds[^1] == ltExportMarker if tok.tokType == tkComment and tok.literal.startsWith("#!nimpretty"): case tok.literal of "#!nimpretty off": inc em.keepIndents - wr("\L") + wrNewline em em.lastLineNumber = tok.line + 1 of "#!nimpretty on": dec em.keepIndents em.lastLineNumber = tok.line - wr("\L") - #for i in 1 .. tok.indent: wr " " - wr tok.literal + wrNewline em + wr em, tok.literal, ltComment em.col = 0 em.lineSpan = 0 return @@ -163,11 +307,15 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) = # we have an inline comment so handle it before the indentation token: emitComment(em, tok) preventComment = true - em.fixedUntil = em.content.high + em.fixedUntil = em.tokens.high elif tok.indent >= 0: - if em.lastTok in (splitters + oprSet) or em.keepIndents > 0: + if em.keepIndents > 0: em.indentLevel = tok.indent + elif (em.lastTok in (splitters + oprSet) and tok.tokType notin closedPars): + # aka: we are in an expression context: + let alignment = tok.indent - em.indentStack[^1] + em.indentLevel = alignment + em.indentStack.high * em.indWidth else: if tok.indent > em.indentStack[^1]: em.indentStack.add tok.indent @@ -186,51 +334,48 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) = is not touched. ]# # remove trailing whitespace: - while em.content.len > 0 and em.content[em.content.high] == ' ': - setLen(em.content, em.content.len-1) - wr("\L") - for i in 2..tok.line - em.lastLineNumber: wr("\L") - em.col = 0 - for i in 1..em.indentLevel: - wr(" ") - em.fixedUntil = em.content.high + removeSpaces em + wrNewline em + for i in 2..tok.line - em.lastLineNumber: wrNewline(em) + wrSpaces em, em.indentLevel + em.fixedUntil = em.tokens.high var lastTokWasTerse = false case tok.tokType of tokKeywordLow..tokKeywordHigh: if endsInAlpha(em): - wr(" ") + wrSpace em elif not em.inquote and not endsInWhite(em) and em.lastTok notin openPars and not em.lastTokWasTerse: #and tok.tokType in oprSet - wr(" ") + wrSpace em if not em.inquote: - wr(TokTypeToStr[tok.tokType]) + wr(em, TokTypeToStr[tok.tokType], ltKeyword) case tok.tokType of tkAnd: rememberSplit(splitAnd) of tkOr: rememberSplit(splitOr) of tkIn, tkNotin: rememberSplit(splitIn) - wr(" ") + wrSpace em else: discard else: # keywords in backticks are not normalized: - wr(tok.ident.s) + wr(em, tok.ident.s, ltIdent) of tkColon: - wr(TokTypeToStr[tok.tokType]) - wr(" ") + wr(em, TokTypeToStr[tok.tokType], ltOther) + wrSpace em of tkSemicolon, tkComma: - wr(TokTypeToStr[tok.tokType]) + wr(em, TokTypeToStr[tok.tokType], ltOther) rememberSplit(splitComma) - wr(" ") + wrSpace em of tkParDotLe, tkParLe, tkBracketDotLe, tkBracketLe, tkCurlyLe, tkCurlyDotLe, tkBracketLeColon: - if tok.strongSpaceA > 0 and not em.endsInWhite: - wr(" ") - wr(TokTypeToStr[tok.tokType]) + if tok.strongSpaceA > 0 and not em.endsInWhite and not em.wasExportMarker: + wrSpace em + wr(em, TokTypeToStr[tok.tokType], ltOther) rememberSplit(splitParLe) of tkParRi, tkBracketRi, tkCurlyRi, @@ -238,33 +383,33 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) = tkCurlyDotRi, tkParDotRi, tkColonColon: - wr(TokTypeToStr[tok.tokType]) + wr(em, TokTypeToStr[tok.tokType], ltOther) of tkDot: lastTokWasTerse = true - wr(TokTypeToStr[tok.tokType]) + wr(em, TokTypeToStr[tok.tokType], ltOther) of tkEquals: - if not em.inquote and not em.endsInWhite: wr(" ") - wr(TokTypeToStr[tok.tokType]) - if not em.inquote: wr(" ") + if not em.inquote and not em.endsInWhite: wrSpace(em) + wr(em, TokTypeToStr[tok.tokType], ltOther) + if not em.inquote: wrSpace(em) of tkOpr, tkDotDot: if ((tok.strongSpaceA == 0 and tok.strongSpaceB == 0) or em.inquote) and tok.ident.s notin ["<", ">", "<=", ">=", "==", "!="]: # bug #9504: remember to not spacify a keyword: lastTokWasTerse = true # if not surrounded by whitespace, don't produce any whitespace either: - wr(tok.ident.s) + wr(em, tok.ident.s, ltOpr) else: - if not em.endsInWhite: wr(" ") - wr(tok.ident.s) + if not em.endsInWhite: wrSpace(em) + wr(em, tok.ident.s, ltOpr) template isUnary(tok): bool = tok.strongSpaceB == 0 and tok.strongSpaceA > 0 if not isUnary(tok): rememberSplit(splitBinary) - wr(" ") + wrSpace(em) of tkAccent: - if not em.inquote and endsInAlpha(em): wr(" ") - wr(TokTypeToStr[tok.tokType]) + if not em.inquote and endsInAlpha(em): wrSpace(em) + wr(em, TokTypeToStr[tok.tokType], ltOther) em.inquote = not em.inquote of tkComment: if not preventComment: @@ -272,36 +417,43 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) = of tkIntLit..tkStrLit, tkRStrLit, tkTripleStrLit, tkGStrLit, tkGTripleStrLit, tkCharLit: let lit = fileSection(em.config, em.fid, tok.offsetA, tok.offsetB) softLinebreak(em, lit) - if endsInAlpha(em) and tok.tokType notin {tkGStrLit, tkGTripleStrLit}: wr(" ") + if endsInAlpha(em) and tok.tokType notin {tkGStrLit, tkGTripleStrLit}: wrSpace(em) em.lineSpan = countNewlines(lit) if em.lineSpan > 0: calcCol(em, lit) - wr lit + wr em, lit, ltLit of tkEof: discard else: let lit = if tok.ident != nil: tok.ident.s else: tok.literal softLinebreak(em, lit) - if endsInAlpha(em): wr(" ") - wr lit + if endsInAlpha(em): wrSpace(em) + wr em, lit, ltIdent em.lastTok = tok.tokType em.lastTokWasTerse = lastTokWasTerse em.lastLineNumber = tok.line + em.lineSpan em.lineSpan = 0 +proc endsWith(em: Emitter; k: varargs[string]): bool = + if em.tokens.len < k.len: return false + for i in 0..high(k): + if em.tokens[em.tokens.len - k.len + i] != k[i]: return false + return true + proc starWasExportMarker*(em: var Emitter) = - if em.content.endsWith(" * "): - setLen(em.content, em.content.len-3) - em.content.add("*") + if em.endsWith(" ", "*", " "): + setLen(em.tokens, em.tokens.len-3) + setLen(em.kinds, em.kinds.len-3) + em.tokens.add("*") + em.kinds.add ltExportMarker dec em.col, 2 proc commaWasSemicolon*(em: var Emitter) = if em.semicolons == detectSemicolonKind: - em.semicolons = if em.content.endsWith(", "): dontTouch else: useSemicolon - if em.semicolons == useSemicolon and em.content.endsWith(", "): - setLen(em.content, em.content.len-2) - em.content.add("; ") + em.semicolons = if em.endsWith(",", " "): dontTouch else: useSemicolon + if em.semicolons == useSemicolon and em.endsWith(",", " "): + em.tokens[em.tokens.len-2] = ";" proc curlyRiWasPragma*(em: var Emitter) = - if em.content.endsWith("}"): - setLen(em.content, em.content.len-1) - em.content.add(".}") + if em.endsWith("}"): + em.tokens[em.tokens.len-1] = ".}" + inc em.col diff --git a/compiler/parser.nim b/compiler/parser.nim index c154e5eb09..eda75f9264 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -30,7 +30,7 @@ import llstream, lexer, idents, strutils, ast, astalgo, msgs, options, lineinfos, pathutils -when defined(nimpretty2): +when defined(nimpretty): import layouter type @@ -44,7 +44,7 @@ type inPragma*: int # Pragma level inSemiStmtList*: int emptyNode: PNode - when defined(nimpretty2): + when defined(nimpretty): em*: Emitter SymbolMode = enum @@ -89,13 +89,20 @@ proc simpleExprAux(p: var TParser, limit: int, mode: TPrimaryMode): PNode # implementation +template prettySection(body) = + when defined(nimpretty): beginSection(p.em) + body + when defined(nimpretty): endSection(p.em) + proc getTok(p: var TParser) = ## Get the next token from the parser's lexer, and store it in the parser's ## `tok` member. rawGetTok(p.lex, p.tok) p.hasProgress = true - when defined(nimpretty2): + when defined(nimpretty): emitTok(p.em, p.lex, p.tok) + # skip the additional tokens that nimpretty needs but the parser has no + # interest in: while p.tok.tokType == tkComment: rawGetTok(p.lex, p.tok) emitTok(p.em, p.lex, p.tok) @@ -106,7 +113,7 @@ proc openParser*(p: var TParser, fileIdx: FileIndex, inputStream: PLLStream, ## initToken(p.tok) openLexer(p.lex, fileIdx, inputStream, cache, config) - when defined(nimpretty2): + when defined(nimpretty): openEmitter(p.em, cache, config, fileIdx) getTok(p) # read the first token p.firstTok = true @@ -119,7 +126,7 @@ proc openParser*(p: var TParser, filename: AbsoluteFile, inputStream: PLLStream, proc closeParser(p: var TParser) = ## Close a parser, freeing up its resources. closeLexer(p.lex) - when defined(nimpretty2): + when defined(nimpretty): closeEmitter(p.em) proc parMessage(p: TParser, msg: TMsgKind, arg = "") = @@ -382,7 +389,7 @@ proc exprColonEqExpr(p: var TParser): PNode = proc exprList(p: var TParser, endTok: TTokType, result: PNode) = #| exprList = expr ^+ comma - when defined(nimpretty2): + when defined(nimpretty): inc p.em.doIndentMore getTok(p) optInd(p, result) @@ -393,7 +400,7 @@ proc exprList(p: var TParser, endTok: TTokType, result: PNode) = if p.tok.tokType != tkComma: break getTok(p) optInd(p, a) - when defined(nimpretty2): + when defined(nimpretty): dec p.em.doIndentMore proc exprColonEqExprListAux(p: var TParser, endTok: TTokType, result: PNode) = @@ -828,10 +835,10 @@ proc simpleExprAux(p: var TParser, limit: int, mode: TPrimaryMode): PNode = result = parseOperators(p, result, limit, mode) proc simpleExpr(p: var TParser, mode = pmNormal): PNode = - when defined(nimpretty2): + when defined(nimpretty): inc p.em.doIndentMore result = simpleExprAux(p, -1, mode) - when defined(nimpretty2): + when defined(nimpretty): dec p.em.doIndentMore proc parseIfExpr(p: var TParser, kind: TNodeKind): PNode = @@ -908,7 +915,7 @@ proc parsePragma(p: var TParser): PNode = skipComment(p, a) optPar(p) if p.tok.tokType in {tkCurlyDotRi, tkCurlyRi}: - when defined(nimpretty2): + when defined(nimpretty): if p.tok.tokType == tkCurlyRi: curlyRiWasPragma(p.em) getTok(p) else: @@ -920,7 +927,7 @@ proc identVis(p: var TParser; allowDot=false): PNode = #| identVisDot = symbol '.' optInd symbol opr? var a = parseSymbol(p) if p.tok.tokType == tkOpr: - when defined(nimpretty2): + when defined(nimpretty): starWasExportMarker(p.em) result = newNodeP(nkPostfix, p) addSon(result, newIdentNodeP(p.tok.ident, p)) @@ -999,7 +1006,7 @@ proc parseTuple(p: var TParser, indentAllowed = false): PNode = var a = parseIdentColonEquals(p, {}) addSon(result, a) if p.tok.tokType notin {tkComma, tkSemiColon}: break - when defined(nimpretty2): + when defined(nimpretty): commaWasSemicolon(p.em) getTok(p) skipComment(p, a) @@ -1035,7 +1042,7 @@ proc parseParamList(p: var TParser, retColon = true): PNode = var a: PNode result = newNodeP(nkFormalParams, p) addSon(result, p.emptyNode) # return type - when defined(nimpretty2): + when defined(nimpretty): inc p.em.doIndentMore inc p.em.keepIndents let hasParLe = p.tok.tokType == tkParLe and p.tok.indent < 0 @@ -1057,7 +1064,7 @@ proc parseParamList(p: var TParser, retColon = true): PNode = break addSon(result, a) if p.tok.tokType notin {tkComma, tkSemiColon}: break - when defined(nimpretty2): + when defined(nimpretty): commaWasSemicolon(p.em) getTok(p) skipComment(p, a) @@ -1072,7 +1079,7 @@ proc parseParamList(p: var TParser, retColon = true): PNode = elif not retColon and not hasParle: # Mark as "not there" in order to mark for deprecation in the semantic pass: result = p.emptyNode - when defined(nimpretty2): + when defined(nimpretty): dec p.em.doIndentMore dec p.em.keepIndents @@ -1235,13 +1242,15 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode = else: result.kind = nkIteratorTy of tkEnum: if mode == pmTypeDef: - result = parseEnum(p) + prettySection: + result = parseEnum(p) else: result = newNodeP(nkEnumTy, p) getTok(p) of tkObject: if mode == pmTypeDef: - result = parseObject(p) + prettySection: + result = parseObject(p) else: result = newNodeP(nkObjectTy, p) getTok(p) @@ -1694,7 +1703,7 @@ proc parseGenericParamList(p: var TParser): PNode = var a = parseGenericParam(p) addSon(result, a) if p.tok.tokType notin {tkComma, tkSemiColon}: break - when defined(nimpretty2): + when defined(nimpretty): commaWasSemicolon(p.em) getTok(p) skipComment(p, a) @@ -2185,10 +2194,16 @@ proc complexOrSimpleStmt(p: var TParser): PNode = result = parseOperators(p, result, -1, pmNormal) else: result = parseSection(p, nkTypeSection, parseTypeDef) - of tkConst: result = parseSection(p, nkConstSection, parseConstant) - of tkLet: result = parseSection(p, nkLetSection, parseVariable) + of tkConst: + prettySection: + result = parseSection(p, nkConstSection, parseConstant) + of tkLet: + prettySection: + result = parseSection(p, nkLetSection, parseVariable) + of tkVar: + prettySection: + result = parseSection(p, nkVarSection, parseVariable) of tkWhen: result = parseIfOrWhen(p, nkWhenStmt) - of tkVar: result = parseSection(p, nkVarSection, parseVariable) of tkBind: result = parseBind(p, nkBindStmt) of tkMixin: result = parseBind(p, nkMixinStmt) of tkUsing: result = parseSection(p, nkUsingStmt, parseVariable) diff --git a/compiler/vm.nim b/compiler/vm.nim index e1e75135a6..a2af0e7b88 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -2031,12 +2031,29 @@ proc evalStaticStmt*(module: PSym; g: ModuleGraph; e: PNode, prc: PSym) = proc setupCompileTimeVar*(module: PSym; g: ModuleGraph; n: PNode) = discard evalConstExprAux(module, g, nil, n, emStaticStmt) +proc prepareVMValue(arg: PNode): PNode = + ## strip nkExprColonExpr from tuple values recurively. That is how + ## they are expected to be stored in the VM. + + # Early abort without copy. No transformation takes place. + if arg.kind in nkLiterals: + return arg + + result = copyNode(arg) + if arg.kind == nkTupleConstr: + for child in arg: + if child.kind == nkExprColonExpr: + result.add prepareVMValue(child[1]) + else: + result.add prepareVMValue(child) + else: + for child in arg: + result.add prepareVMValue(child) + proc setupMacroParam(x: PNode, typ: PType): TFullReg = case typ.kind of tyStatic: - putIntoReg(result, x) - #of tyTypeDesc: - # putIntoReg(result, x) + putIntoReg(result, prepareVMValue(x)) else: result.kind = rkNode var n = x diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 035ab25cdd..de314a552b 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -813,44 +813,28 @@ proc genCard(c: PCtx; n: PNode; dest: var TDest) = proc genCastIntFloat(c: PCtx; n: PNode; dest: var TDest) = const allowedIntegers = {tyInt..tyInt64, tyUInt..tyUInt64, tyChar} - var signedIntegers = {tyInt8..tyInt32} - var unsignedIntegers = {tyUInt8..tyUInt32, tyChar} + var signedIntegers = {tyInt..tyInt64} + var unsignedIntegers = {tyUInt..tyUInt64, tyChar} 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) - if c.config.target.intSize < 8: - signedIntegers.incl(tyInt) - unsignedIntegers.incl(tyUInt) - if src_size == dst_size and src.kind in allowedIntegers and - dst.kind in allowedIntegers: + if src.kind in allowedIntegers and dst.kind in allowedIntegers: let tmp = c.genx(n.sons[1]) - var tmp2 = c.getTemp(n.sons[1].typ) - let tmp3 = c.getTemp(n.sons[1].typ) if dest < 0: dest = c.getTemp(n[0].typ) - proc mkIntLit(ival: int): int = - result = genLiteral(c, newIntTypeNode(nkIntLit, ival, getSysType(c.graph, n.info, tyInt))) - if src.kind in unsignedIntegers and dst.kind in signedIntegers: - # cast unsigned to signed integer of same size - # signedVal = (unsignedVal xor offset) -% offset - let offset = 1 shl (src_size * 8 - 1) - c.gABx(n, opcLdConst, tmp2, mkIntLit(offset)) - c.gABC(n, opcBitxorInt, tmp3, tmp, tmp2) - c.gABC(n, opcSubInt, dest, tmp3, tmp2) - elif src.kind in signedIntegers and dst.kind in unsignedIntegers: - # cast signed to unsigned integer of same size - # unsignedVal = (offset +% signedVal +% 1) and offset - let offset = (1 shl (src_size * 8)) - 1 - c.gABx(n, opcLdConst, tmp2, mkIntLit(offset)) - c.gABx(n, opcLdConst, dest, mkIntLit(offset+1)) - c.gABC(n, opcAddu, tmp3, tmp, dest) - c.gABC(n, opcNarrowU, tmp3, TRegister(src_size*8)) - c.gABC(n, opcBitandInt, dest, tmp3, tmp2) - else: - c.gABC(n, opcAsgnInt, dest, tmp) + c.gABC(n, opcAsgnInt, dest, tmp) + if dst_size != sizeof(BiggestInt): # don't do anything on biggest int types + if dst.kind in signedIntegers: # we need to do sign extensions + if dst_size <= src_size: + # Sign extension can be omitted when the size increases. + c.gABC(n, opcSignExtend, dest, TRegister(dst_size*8)) + elif dst.kind in unsignedIntegers: + if src.kind in signedIntegers or dst_size < src_size: + # Cast from signed to unsigned always needs narrowing. Cast + # from unsigned to unsigned only needs narrowing when target + # is smaller than source. + c.gABC(n, opcNarrowU, dest, TRegister(dst_size*8)) c.freeTemp(tmp) - c.freeTemp(tmp2) - c.freeTemp(tmp3) elif src_size == dst_size and src.kind in allowedIntegers and dst.kind in {tyFloat, tyFloat32, tyFloat64}: let tmp = c.genx(n[1]) diff --git a/config/nimdoc.tex.cfg b/config/nimdoc.tex.cfg index 599ede345a..76fef4afa4 100644 --- a/config/nimdoc.tex.cfg +++ b/config/nimdoc.tex.cfg @@ -39,7 +39,7 @@ $content """ doc.file = """ -% This file was generated by Nimrod. +% This file was generated by Nim. % Generated: $date $time UTC \documentclass[a4paper]{article} \usepackage[left=2cm,right=3cm,top=3cm,bottom=3cm]{geometry} diff --git a/doc/basicopt.txt b/doc/basicopt.txt index aa6675dc8e..1405e7b01b 100644 --- a/doc/basicopt.txt +++ b/doc/basicopt.txt @@ -24,7 +24,7 @@ Options: -a, --assertions:on|off turn assertions on|off --opt:none|speed|size optimize not at all or for speed|size Note: use -d:release for a release build! - --debugger:native|endb use native debugger (gdb) | ENDB (experimental) + --debugger:native Use native debugger (gdb) --app:console|gui|lib|staticlib generate a console app|GUI app|DLL|static library -r, --run run the compiled program with given arguments diff --git a/doc/contributing.rst b/doc/contributing.rst index c240cb80ed..317fe3ab7c 100644 --- a/doc/contributing.rst +++ b/doc/contributing.rst @@ -343,12 +343,10 @@ The Git stuff General commit rules -------------------- -1. Bugfixes that should be backported to the latest stable release should - contain the string ``[backport]`` in the commit message! There will be an - outmated process relying on these. However, bugfixes also have the inherent - risk of causing regressions which are worse for a "stable, bugfixes-only" - branch, so in doubt, leave out the ``[backport]``. Standard library bugfixes - are less critical than compiler bugfixes. +1. The commit message should contain either ``[bugfix]`` or ``[feature]`` + or ``[refactoring]`` or ``[other]``. Refactorings and bugfixes and "other" + are backported to the latest stable release branch (currently 0.20.x). + Refactorings are backported because they often enable further bugfixes. 2. All changes introduced by the commit (diff lines) must be related to the subject of the commit. diff --git a/doc/nimc.rst b/doc/nimc.rst index d291992ff1..4f5808cd24 100644 --- a/doc/nimc.rst +++ b/doc/nimc.rst @@ -132,8 +132,8 @@ Through the ``-d:x`` or ``--define:x`` switch you can define compile time symbols for conditional compilation. The defined switches can be checked in source code with the `when statement `_ and `defined proc `_. The typical use of this switch is to -enable builds in release mode (``-d:release``) where certain safety checks are -omitted for better performance. Another common use is the ``-d:ssl`` switch to +enable builds in release mode (``-d:release``) where optimizations are +enabled for better performance. Another common use is the ``-d:ssl`` switch to activate SSL sockets. Additionally, you may pass a value along with the symbol: ``-d:x=y`` @@ -169,6 +169,10 @@ The default build of a project is a `debug build`:idx:. To compile a nim c -d:release myproject.nim + To compile a `dangerous release build`:idx: define the ``danger`` symbol:: + + nim c -d:danger myproject.nim + Search path handling -------------------- @@ -344,10 +348,10 @@ complete list. ====================== ========================================================= Define Effect ====================== ========================================================= -``release`` Turns off runtime checks and turns on the optimizer. +``release`` Turns on the optimizer. More aggressive optimizations are possible, eg: ``--passC:-ffast-math`` (but see issue #10305) - ``--stacktrace:off`` +``danger`` Turns off all runtime checks and turns on the optimizer. ``useFork`` Makes ``osproc`` use ``fork`` instead of ``posix_spawn``. ``useNimRtl`` Compile and link against ``nimrtl.dll``. ``useMalloc`` Makes Nim use C's `malloc`:idx: instead of Nim's diff --git a/doc/nimdoc.css b/doc/nimdoc.css index a002b6be1a..a8be5756b4 100644 --- a/doc/nimdoc.css +++ b/doc/nimdoc.css @@ -17,7 +17,7 @@ customize this style sheet. */ /* - Modified for the Nimrod Documenation by + Modified for the Nim Documenation by Andreas Rumpf */ diff --git a/koch.nim b/koch.nim index a2c1f16736..9f2e232904 100644 --- a/koch.nim +++ b/koch.nim @@ -111,7 +111,7 @@ proc overwriteFile(source, dest: string) = proc copyExe(source, dest: string) = safeRemove(dest) copyFile(dest=dest, source=source) - inclFilePermissions(dest, {fpUserExec}) + inclFilePermissions(dest, {fpUserExec, fpGroupExec, fpOthersExec}) const compileNimInst = "tools/niminst/niminst" diff --git a/lib/pure/complex.nim b/lib/pure/complex.nim index 69d9c0f7f4..442bf62dbb 100644 --- a/lib/pure/complex.nim +++ b/lib/pure/complex.nim @@ -7,10 +7,6 @@ # distribution, for details about the copyright. # - - - - ## This module implements complex numbers. ## Complex numbers are currently implemented as generic on a 64-bit or 32-bit float. @@ -42,8 +38,8 @@ proc complex64*(re: float64; im: float64 = 0.0): Complex[float64] = 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) +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``. diff --git a/lib/pure/hashes.nim b/lib/pure/hashes.nim index df29dc0802..c8b9f0e069 100644 --- a/lib/pure/hashes.nim +++ b/lib/pure/hashes.nim @@ -149,6 +149,12 @@ proc hash*(x: float): Hash {.inline.} = var y = x + 1.0 result = cast[ptr Hash](addr(y))[] + +# Forward declarations before methods that hash containers. This allows +# containers to contain other containers +proc hash*[A](x: openArray[A]): Hash +proc hash*[A](x: set[A]): Hash + template bytewiseHashing(result: Hash, x: typed, start, stop: int) = for i in start .. stop: result = result !& hash(x[i]) @@ -292,12 +298,6 @@ proc hashIgnoreCase*(sBuf: string, sPos, ePos: int): Hash = result = !$h -# Forward declarations before methods that hash containers. This allows -# containers to contain other containers -proc hash*[A](x: openArray[A]): Hash -proc hash*[A](x: set[A]): Hash - - proc hash*[T: tuple](x: T): Hash = ## Efficient hashing of tuples. for f in fields(x): diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim index 83cf77a1b1..4139d83fd3 100644 --- a/lib/pure/httpclient.nim +++ b/lib/pure/httpclient.nim @@ -121,12 +121,59 @@ ## if however data does not reach the client within the specified timeout a ## ``TimeoutError`` exception will be raised. ## +## Here is how to set a timeout when creating an ``HttpClient`` instance: +## +## .. code-block:: Nim +## import httpclient +## +## let client = newHttpClient(timeout = 42) +## ## Proxy ## ===== ## ## A proxy can be specified as a param to any of the procedures defined in ## this module. To do this, use the ``newProxy`` constructor. Unfortunately, ## only basic authentication is supported at the moment. +## +## Some examples on how to configure a Proxy for ``HttpClient``: +## +## .. code-block:: Nim +## import httpclient +## +## let myProxy = newProxy("http://myproxy.network") +## let client = newHttpClient(proxy = myProxy) +## +## Get Proxy URL from environment variables: +## +## .. code-block:: Nim +## import httpclient +## +## var url = "" +## try: +## if existsEnv("http_proxy"): +## url = getEnv("http_proxy") +## elif existsEnv("https_proxy"): +## url = getEnv("https_proxy") +## except ValueError: +## echo "Unable to parse proxy from environment variables." +## +## let myProxy = newProxy(url = url) +## let client = newHttpClient(proxy = myProxy) +## +## Redirects +## ========= +## +## The maximum redirects can be set with the ``maxRedirects`` of ``int`` type, +## it specifies the maximum amount of redirects to follow, +## it defaults to ``5``, you can set it to ``0`` to disable redirects. +## +## Here you can see an example about how to set the ``maxRedirects`` of ``HttpClient``: +## +## .. code-block:: Nim +## import httpclient +## +## let client = newHttpClient(maxRedirects = 0) +## import net, strutils, uri, parseutils, strtabs, base64, os, mimetypes, math, random, httpcore, times, tables, streams @@ -647,7 +694,7 @@ proc getSocket*(client: HttpClient): Socket = ## ## .. code-block:: Nim ## if client.connected: - ## echo client.getSocket.getLocalAddr + ## echo client.getSocket.getLocalAddr ## echo client.getSocket.getPeerAddr ## return client.socket diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 8a185e81c0..79a24662db 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -798,12 +798,16 @@ proc getTempDir*(): string {.rtl, extern: "nos$1", ## * `expandTilde proc <#expandTilde,string>`_ ## * `getCurrentDir proc <#getCurrentDir>`_ ## * `setCurrentDir proc <#setCurrentDir,string>`_ + const tempDirDefault = "/tmp" + result = tempDirDefault 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/" + const tempDir {.strdefine.}: string = tempDirDefault + result = tempDir + elif defined(windows): result = string(getEnv("TEMP")) + elif defined(android): result = getHomeDir() + else: + if existsEnv("TMPDIR"): result = string(getEnv("TMPDIR")) + normalizePathEnd(result, trailingSep=true) proc expandTilde*(path: string): string {. tags: [ReadEnvEffect, ReadIOEffect].} = diff --git a/lib/pure/streams.nim b/lib/pure/streams.nim index f1a6a6aeb8..28d9d7612f 100644 --- a/lib/pure/streams.nim +++ b/lib/pure/streams.nim @@ -1241,7 +1241,7 @@ when not defined(js): if open(f, filename, mode, bufSize): return newFileStream(f) else: - raise newEIO("cannot open file\n") + raise newEIO("cannot open file stream: " & filename) when false: type diff --git a/lib/system.nim b/lib/system.nim index 08bb5645d9..d20beabf0e 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -4450,9 +4450,8 @@ when defined(genode): import system/widestrs export widestrs -when not defined(nimnoio): - import system/io - export io +import system/io +export io when not defined(createNimHcr): include nimhcr diff --git a/lib/system/io.nim.cfg b/lib/system/io.nim.cfg deleted file mode 100644 index fd7cc755ce..0000000000 --- a/lib/system/io.nim.cfg +++ /dev/null @@ -1 +0,0 @@ ---define: nimnoio diff --git a/nimpretty/nimpretty.nim b/nimpretty/nimpretty.nim index 3e06ccff35..185f8da7c3 100644 --- a/nimpretty/nimpretty.nim +++ b/nimpretty/nimpretty.nim @@ -50,15 +50,11 @@ proc prettyPrint(infile, outfile: string, opt: PrettyOptions) = var conf = newConfigRef() let fileIdx = fileInfoIdx(conf, AbsoluteFile infile) conf.outFile = RelativeFile outfile - when defined(nimpretty2): - 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) + var p: TParsers + p.parser.em.indWidth = opt.indWidth + if setupParsers(p, fileIdx, newIdentCache(), conf): + discard parseAll(p) + closeParsers(p) proc main = var infile, outfile: string diff --git a/nimpretty/nimpretty.nim.cfg b/nimpretty/nimpretty.nim.cfg index b4fac53776..138bce52e2 100644 --- a/nimpretty/nimpretty.nim.cfg +++ b/nimpretty/nimpretty.nim.cfg @@ -1,3 +1,2 @@ --define: nimpretty ---define: nimpretty2 --define: nimOldCaseObjects diff --git a/nimpretty/tests/exhaustive.nim b/nimpretty/tests/exhaustive.nim index 2903a59afa..2f8fe03f28 100644 --- a/nimpretty/tests/exhaustive.nim +++ b/nimpretty/tests/exhaustive.nim @@ -366,3 +366,46 @@ proc fun4() = var i = 0 while i/dev/null | openssl x509 -noout -dates") + +let res = execProcess("echo | openssl s_client -connect example.com:443 2>/dev/null | openssl x509 -noout -dates") + + +# bug #10177 + +proc foo * () = + discard + +proc foo* [T]() = + discard + + +# bug #10159 + +proc fun() = + discard + +proc main() = + echo "foo"; echo "bar"; + discard + +main() + +type + TCallingConvention* = enum + ccDefault, # proc has no explicit calling convention + ccStdCall, # procedure is stdcall + ccCDecl, # cdecl + ccSafeCall, # safecall + ccSysCall, # system call + ccInline, # proc should be inlined + ccNoInline, # proc should not be inlined + ccFastCall, # fastcall (pass parameters in registers) + ccClosure, # proc has a closure + ccNoConvention # needed for generating proper C procs sometimes diff --git a/nimpretty/tests/expected/exhaustive.nim b/nimpretty/tests/expected/exhaustive.nim index cd3d136e7a..75580081d6 100644 --- a/nimpretty/tests/expected/exhaustive.nim +++ b/nimpretty/tests/expected/exhaustive.nim @@ -29,10 +29,10 @@ var x = 1 type GeneralTokenizer* = object of RootObj ## comment here - kind*: TokenClass ## and here - start*, length*: int ## you know how it goes... + kind*: TokenClass ## and here + start*, length*: int ## you know how it goes... buf: cstring - pos: int # other comment here. + pos: int # other comment here. state: TokenClass var x*: string @@ -122,7 +122,7 @@ type inquote {.pragmaHereWrongCurlyEnd.}: bool col, lastLineNumber, lineSpan, indentLevel: int content: string - fixedUntil: int # marks where we must not go in the content + fixedUntil: int # marks where we must not go in the content altSplitPos: array[SplitKind, int] # alternative split positions proc openEmitter*[T, S](em: var Emitter; config: ConfigRef; @@ -375,3 +375,47 @@ proc fun4() = var i = 0 while i < a.len and i < a.len: return + + +# bug #10295 + +import osproc +let res = execProcess( + "echo | openssl s_client -connect example.com:443 2>/dev/null | openssl x509 -noout -dates") + +let res = execProcess( + "echo | openssl s_client -connect example.com:443 2>/dev/null | openssl x509 -noout -dates") + + +# bug #10177 + +proc foo*() = + discard + +proc foo*[T]() = + discard + + +# bug #10159 + +proc fun() = + discard + +proc main() = + echo "foo"; echo "bar"; + discard + +main() + +type + TCallingConvention* = enum + ccDefault, # proc has no explicit calling convention + ccStdCall, # procedure is stdcall + ccCDecl, # cdecl + ccSafeCall, # safecall + ccSysCall, # system call + ccInline, # proc should be inlined + ccNoInline, # proc should not be inlined + ccFastCall, # fastcall (pass parameters in registers) + ccClosure, # proc has a closure + ccNoConvention # needed for generating proper C procs sometimes diff --git a/nimpretty/tests/expected/wrong_ind.nim b/nimpretty/tests/expected/wrong_ind.nim new file mode 100644 index 0000000000..9855e0c4d7 --- /dev/null +++ b/nimpretty/tests/expected/wrong_ind.nim @@ -0,0 +1,73 @@ + +# bug #9505 + +import std/[ + strutils, ospaths, os +] +import pkg/[ + regex +] + +proc fun() = + let a = [ + 1, + 2, + ] + discard + +proc funB() = + let a = [ + 1, + 2, + 3 + ] + discard + + +# bug #10156 +proc foo = + ## Comment 1 + ## Comment 2 + discard + +proc bar = + ## Comment 3 + ## Comment 4 + ## More here. + discard + + +proc barB = + # Comment 5 + # Comment 6 + discard + + +var x: int = 2 + +echo x +# bug #9144 + +proc a() = + if cond: + while true: + discard + # comment 1 + # end while + #end if + + # comment 2 + #if + #case + #end case + #end if + discard + + +proc a() = + while true: + discard + # comment 1 + + # comment 2 + discard diff --git a/nimpretty/tests/wrong_ind.nim b/nimpretty/tests/wrong_ind.nim new file mode 100644 index 0000000000..eaa561b516 --- /dev/null +++ b/nimpretty/tests/wrong_ind.nim @@ -0,0 +1,73 @@ + +# bug #9505 + +import std/[ + strutils, ospaths, os +] +import pkg/[ + regex +] + +proc fun() = + let a = [ + 1, + 2, + ] + discard + +proc funB() = + let a = [ + 1, + 2, + 3 + ] + discard + + +# bug #10156 +proc foo = + ## Comment 1 + ## Comment 2 + discard + +proc bar = + ## Comment 3 + ## Comment 4 + ## More here. + discard + + +proc barB = + # Comment 5 + # Comment 6 + discard + + +var x: int = 2 + +echo x +# bug #9144 + +proc a() = + if cond: + while true: + discard + # comment 1 + # end while + #end if + + # comment 2 + #if + #case + #end case + #end if + discard + + +proc a() = + while true: + discard + # comment 1 + + # comment 2 + discard diff --git a/testament/categories.nim b/testament/categories.nim index 4fddeb5548..3db90d15f1 100644 --- a/testament/categories.nim +++ b/testament/categories.nim @@ -474,11 +474,7 @@ iterator listPackages(): tuple[name, url, cmd: string, hasDeps: bool] = for n, cmd, hasDeps, url in important_packages.packages.items: let cmd = if cmd.len == 0: defaultCmd else: cmd if url.len != 0: - if hasDeps: - # use url instead of name, so we can do 'nimble install' - yield (url, url, cmd, hasDeps) - else: - yield (n, url, cmd, hasDeps) + yield (n, url, cmd, hasDeps) else: var found = false for package in packageList.items: @@ -515,7 +511,8 @@ proc testNimblePackages(r: var TResults, cat: Category) = let buildPath = packagesDir / name if not existsDir(buildPath): if hasDep: - let (nimbleCmdLine, nimbleOutput, nimbleStatus) = execCmdEx2("nimble", ["install", "-y", name]) + let installName = if url.len != 0: url else: name + let (nimbleCmdLine, nimbleOutput, nimbleStatus) = execCmdEx2("nimble", ["install", "-y", installName]) if nimbleStatus != QuitSuccess: let message = "nimble install failed:\n$ " & nimbleCmdLine & "\n" & nimbleOutput r.addResult(test, targetC, "", message, reInstallFailed) diff --git a/testament/important_packages.nim b/testament/important_packages.nim index fbd97c96d6..049b50e72e 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -24,6 +24,7 @@ pkg "comprehension", "", false, "https://github.com/alehander42/comprehension" pkg "criterion" pkg "dashing", "nim c tests/functional.nim" pkg "docopt" +pkg "easygl", "nim c -o:egl -r src/easygl.nim", true, "https://github.com/jackmott/easygl" pkg "fragments", "nim c -r fragments/dsl.nim" pkg "gara" pkg "glob" @@ -49,17 +50,17 @@ pkg "nimfp", "nim c -o:nfp -r src/fp.nim", true pkg "nimgame2", "nim c nimgame2/nimgame.nim", true pkg "nimgen", "nim c -o:nimgenn -r src/nimgen/runcfg.nim", true # pkg "nimlsp", "", true -# pkg "nimly", "nim c -r tests/test_nimly", true +pkg "nimly", "nim c -r tests/test_nimly", true # pkg "nimongo", "nimble test_ci", true pkg "nimpy", "nim c -r tests/nimfrompy.nim" pkg "nimquery" pkg "nimsl", "", true pkg "nimsvg" pkg "nimx", "nim c --threads:on test/main.nim", true -pkg "norm", "nim c -o:normm src/norm.nim" +# pkg "norm", "nim c -o:normm src/norm.nim", true pkg "npeg" pkg "ormin", "nim c -o:orminn ormin.nim", true -#pkg "parsetoml" +pkg "parsetoml" pkg "patty" pkg "plotly", "nim c examples/all.nim", true pkg "protobuf", "nim c -o:protobuff -r src/protobuf.nim", true @@ -68,11 +69,15 @@ pkg "result", "nim c -r result.nim" pkg "rosencrantz", "nim c -o:rsncntz -r rosencrantz.nim" pkg "sdl1", "nim c -r src/sdl.nim" pkg "sdl2_nim", "nim c -r sdl2/sdl.nim" +pkg "snip", "", false, "https://github.com/genotrance/snip" pkg "stint", "nim c -o:stintt -r stint.nim" pkg "strunicode", "nim c -r src/strunicode.nim", true +pkg "telebot", "nim c -o:tbot -r telebot.nim", true pkg "tiny_sqlite" pkg "unicodedb" pkg "unicodeplus", "", true pkg "unpack" +pkg "with" +# pkg "winim", "", true pkg "yaml" pkg "zero_functional", "nim c -r test.nim" diff --git a/tests/destructor/tsimpletable.nim b/tests/destructor/tsimpletable.nim new file mode 100644 index 0000000000..36546b9f6a --- /dev/null +++ b/tests/destructor/tsimpletable.nim @@ -0,0 +1,24 @@ +discard """ + cmd: '''nim c --newruntime $file''' + output: '''(field: "value") +3 3 new: 0''' +""" + +import core / allocators +import system / ansi_c + +import tables + +type + Node = ref object + field: string + +proc main = + var w = newTable[string, owned Node]() + w["key"] = Node(field: "value") + echo w["key"][] + +main() + +let (a, d) = allocCounters() +discard cprintf("%ld %ld new: %ld\n", a, d, allocs) diff --git a/tests/macros/tmacros1.nim b/tests/macros/tmacros1.nim index a50465e1cf..706281d133 100644 --- a/tests/macros/tmacros1.nim +++ b/tests/macros/tmacros1.nim @@ -49,3 +49,15 @@ myEnums = enumerators2() echo myEnums myEnums = enumerators3() echo myEnums + +#10751 + +type Tuple = tuple + a: string + b: int + +macro foo(t: static Tuple): untyped = + doAssert t.a == "foo" + doAssert t.b == 12345 + +foo((a: "foo", b: 12345)) diff --git a/tests/stdlib/tos.nim b/tests/stdlib/tos.nim index 23fa4d098a..2d69332fcf 100644 --- a/tests/stdlib/tos.nim +++ b/tests/stdlib/tos.nim @@ -333,3 +333,15 @@ block ospaths: doAssert joinPath("", "lib") == "lib" doAssert joinPath("", "/lib") == unixToNativePath"/lib" doAssert joinPath("usr/", "/lib") == unixToNativePath"usr/lib" + +block getTempDir: + block TMPDIR: + # TMPDIR env var is not used if either of these are defined. + when not (defined(tempDir) or defined(windows) or defined(android)): + if existsEnv("TMPDIR"): + let origTmpDir = getEnv("TMPDIR") + putEnv("TMPDIR", "/mytmp") + doAssert getTempDir() == "/mytmp/" + putEnv("TMPDIR", origTmpDir) + else: + doAssert getTempDir() == "/tmp/" diff --git a/tests/vm/tcastint.nim b/tests/vm/tcastint.nim index 437342a74b..c64dbc666e 100644 --- a/tests/vm/tcastint.nim +++ b/tests/vm/tcastint.nim @@ -2,6 +2,8 @@ discard """ output: "OK" """ +import macros + type Dollar = distinct int XCoord = distinct int32 @@ -112,6 +114,130 @@ proc test() = doAssert(not compiles(cast[uint32](I8))) doAssert(not compiles(cast[uint64](I8))) +const prerecordedResults = [ + # cast to char + "\0", "\255", + "\0", "\255", + "\0", "\255", + "\0", "\255", + "\0", "\255", + "\128", "\127", + "\0", "\255", + "\0", "\255", + "\0", "\255", + # cast to uint8 + "0", "255", + "0", "255", + "0", "255", + "0", "255", + "0", "255", + "128", "127", + "0", "255", + "0", "255", + "0", "255", + # cast to uint16 + "0", "255", + "0", "255", + "0", "65535", + "0", "65535", + "0", "65535", + "65408", "127", + "32768", "32767", + "0", "65535", + "0", "65535", + # cast to uint32 + "0", "255", + "0", "255", + "0", "65535", + "0", "4294967295", + "0", "4294967295", + "4294967168", "127", + "4294934528", "32767", + "2147483648", "2147483647", + "0", "4294967295", + # cast to uint64 + "0", "255", + "0", "255", + "0", "65535", + "0", "4294967295", + "0", "18446744073709551615", + "18446744073709551488", "127", + "18446744073709518848", "32767", + "18446744071562067968", "2147483647", + "9223372036854775808", "9223372036854775807", + # cast to int8 + "0", "-1", + "0", "-1", + "0", "-1", + "0", "-1", + "0", "-1", + "-128", "127", + "0", "-1", + "0", "-1", + "0", "-1", + # cast to int16 + "0", "255", + "0", "255", + "0", "-1", + "0", "-1", + "0", "-1", + "-128", "127", + "-32768", "32767", + "0", "-1", + "0", "-1", + # cast to int32 + "0", "255", + "0", "255", + "0", "65535", + "0", "-1", + "0", "-1", + "-128", "127", + "-32768", "32767", + "-2147483648", "2147483647", + "0", "-1", + # cast to int64 + "0", "255", + "0", "255", + "0", "65535", + "0", "4294967295", + "0", "-1", + "-128", "127", + "-32768", "32767", + "-2147483648", "2147483647", + "-9223372036854775808", "9223372036854775807", +] + +proc free_integer_casting() = + # cast from every integer type to every type and ensure same + # behavior in vm and execution time. + macro bar(arg: untyped) = + result = newStmtList() + var i = 0 + for it1 in arg: + let typA = it1[0] + for it2 in arg: + let lowB = it2[1] + let highB = it2[2] + let castExpr1 = nnkCast.newTree(typA, lowB) + let castExpr2 = nnkCast.newTree(typA, highB) + let lit1 = newLit(prerecordedResults[i*2]) + let lit2 = newLit(prerecordedResults[i*2+1]) + result.add quote do: + doAssert($(`castExpr1`) == `lit1`) + doAssert($(`castExpr2`) == `lit2`) + i += 1 + + bar([ + (char, '\0', '\255'), + (uint8, 0'u8, 0xff'u8), + (uint16, 0'u16, 0xffff'u16), + (uint32, 0'u32, 0xffffffff'u32), + (uint64, 0'u64, 0xffffffffffffffff'u64), + (int8, 0x80'i8, 0x7f'i8), + (int16, 0x8000'i16, 0x7fff'i16), + (int32, 0x80000000'i32, 0x7fffffff'i32), + (int64, 0x8000000000000000'i64, 0x7fffffffffffffff'i64) + ]) proc test_float_cast = @@ -158,9 +284,11 @@ proc test_float32_cast = test() test_float_cast() test_float32_cast() +free_integer_casting() static: test() test_float_cast() test_float32_cast() + free_integer_casting() echo "OK" diff --git a/tools/nim.zsh-completion b/tools/nim.zsh-completion index e9b9002fb5..38ed93f736 100644 --- a/tools/nim.zsh-completion +++ b/tools/nim.zsh-completion @@ -63,7 +63,7 @@ _nim() { '*--opt=none[do not optimize]' \ '*--opt=speed[optimize for speed|size - use -d:release for a release build]' \ '*--opt=size[optimize for size]' \ - '*--debugger:native|endb[use native debugger (gdb) | ENDB (experimental)]' \ + '*--debugger:native[use native debugger (gdb)]' \ '*--app=console[generate a console app]' \ '*--app=gui[generate a GUI app]' \ '*--app=lib[generate a dynamic library]' \