From 7819e63f7704424625c55d128e874b4ba5736f65 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sat, 16 Jun 2018 09:53:36 +0200 Subject: [PATCH 01/35] nimpretty: more features --- compiler/layouter.nim | 38 +++++++++++++++++++++++++++----------- compiler/parser.nim | 26 ++++++++++++++++++++++++-- 2 files changed, 51 insertions(+), 13 deletions(-) diff --git a/compiler/layouter.nim b/compiler/layouter.nim index 90e9d6fd79..6a9a12d821 100644 --- a/compiler/layouter.nim +++ b/compiler/layouter.nim @@ -9,7 +9,6 @@ ## Layouter for nimpretty. Still primitive but useful. ## TODO -## - Fix 'echo ()' vs 'echo()' difference! ## - Make indentations consistent. ## - Align 'if' and 'case' expressions properly. @@ -31,6 +30,7 @@ type lastTok: TTokType inquote: bool col, lastLineNumber, lineSpan, indentLevel: int + doIndentMore*: int content: string fixedUntil: int # marks where we must not go in the content altSplitPos: array[SplitKind, int] # alternative split positions @@ -77,6 +77,8 @@ template rememberSplit(kind) = if goodCol(em.col): em.altSplitPos[kind] = em.content.len +template moreIndent(em): int = (if em.doIndentMore > 0: 4 else: 2) + proc softLinebreak(em: var Emitter, lit: string) = # XXX Use an algorithm that is outlined here: # https://llvm.org/devmtg/2013-04/jasper-slides.pdf @@ -85,12 +87,12 @@ proc softLinebreak(em: var Emitter, lit: string) = if em.lastTok in splitters: wr("\L") em.col = 0 - for i in 1..em.indentLevel+2: wr(" ") + for i in 1..em.indentLevel+moreIndent(em): wr(" ") else: # search backwards for a good split position: for a in em.altSplitPos: if a > em.fixedUntil: - let ws = "\L" & repeat(' ',em.indentLevel+2) + let ws = "\L" & repeat(' ',em.indentLevel+moreIndent(em)) em.col = em.content.len - a em.content.insert(ws, a) break @@ -155,15 +157,19 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) = wr(TokTypeToStr[tok.tokType]) wr(" ") rememberSplit(splitComma) - of tkParLe, tkParRi, tkBracketLe, - tkBracketRi, tkCurlyLe, tkCurlyRi, - tkBracketDotLe, tkBracketDotRi, - tkCurlyDotLe, tkCurlyDotRi, - tkParDotLe, tkParDotRi, - tkColonColon, tkDot, tkBracketLeColon: + of tkParDotLe, tkParLe, tkBracketDotLe, tkBracketLe, + tkCurlyLe, tkCurlyDotLe, tkBracketLeColon: + if tok.strongSpaceA > 0 and not em.endsInWhite: + wr(" ") + wr(TokTypeToStr[tok.tokType]) + rememberSplit(splitParLe) + of tkParRi, + tkBracketRi, tkCurlyRi, + tkBracketDotRi, + tkCurlyDotRi, + tkParDotRi, + tkColonColon, tkDot: wr(TokTypeToStr[tok.tokType]) - if tok.tokType in splitters: - rememberSplit(splitParLe) of tkEquals: if not em.endsInWhite: wr(" ") wr(TokTypeToStr[tok.tokType]) @@ -206,3 +212,13 @@ proc starWasExportMarker*(em: var Emitter) = setLen(em.content, em.content.len-3) em.content.add("*") dec em.col, 2 + +proc commaWasSemicolon*(em: var Emitter) = + if em.content.endsWith(", "): + setLen(em.content, em.content.len-2) + em.content.add("; ") + +proc curlyRiWasPragma*(em: var Emitter) = + if em.content.endsWith("}"): + setLen(em.content, em.content.len-1) + em.content.add(".}") diff --git a/compiler/parser.nim b/compiler/parser.nim index c0465b05ea..9a9fed0cfd 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -408,6 +408,8 @@ proc exprColonEqExpr(p: var TParser): PNode = proc exprList(p: var TParser, endTok: TTokType, result: PNode) = #| exprList = expr ^+ comma + when defined(nimpretty2): + inc p.em.doIndentMore getTok(p) optInd(p, result) # progress guaranteed @@ -417,6 +419,8 @@ proc exprList(p: var TParser, endTok: TTokType, result: PNode) = if p.tok.tokType != tkComma: break getTok(p) optInd(p, a) + when defined(nimpretty2): + dec p.em.doIndentMore proc exprColonEqExprListAux(p: var TParser, endTok: TTokType, result: PNode) = assert(endTok in {tkCurlyRi, tkCurlyDotRi, tkBracketRi, tkParRi}) @@ -837,7 +841,11 @@ 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): + inc p.em.doIndentMore result = simpleExprAux(p, -1, mode) + when defined(nimpretty2): + dec p.em.doIndentMore proc parseIfExpr(p: var TParser, kind: TNodeKind): PNode = #| condExpr = expr colcom expr optInd @@ -912,8 +920,12 @@ proc parsePragma(p: var TParser): PNode = getTok(p) skipComment(p, a) optPar(p) - if p.tok.tokType in {tkCurlyDotRi, tkCurlyRi}: getTok(p) - else: parMessage(p, "expected '.}'") + if p.tok.tokType in {tkCurlyDotRi, tkCurlyRi}: + when defined(nimpretty2): + curlyRiWasPragma(p.em) + getTok(p) + else: + parMessage(p, "expected '.}'") dec p.inPragma proc identVis(p: var TParser; allowDot=false): PNode = @@ -1000,6 +1012,8 @@ 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): + commaWasSemicolon(p.em) getTok(p) skipComment(p, a) optPar(p) @@ -1034,6 +1048,8 @@ proc parseParamList(p: var TParser, retColon = true): PNode = var a: PNode result = newNodeP(nkFormalParams, p) addSon(result, p.emptyNode) # return type + when defined(nimpretty2): + inc p.em.doIndentMore let hasParLe = p.tok.tokType == tkParLe and p.tok.indent < 0 if hasParLe: getTok(p) @@ -1053,6 +1069,8 @@ proc parseParamList(p: var TParser, retColon = true): PNode = break addSon(result, a) if p.tok.tokType notin {tkComma, tkSemiColon}: break + when defined(nimpretty2): + commaWasSemicolon(p.em) getTok(p) skipComment(p, a) optPar(p) @@ -1066,6 +1084,8 @@ 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): + dec p.em.doIndentMore proc optPragmas(p: var TParser): PNode = if p.tok.tokType == tkCurlyDotLe and (p.tok.indent < 0 or realInd(p)): @@ -1668,6 +1688,8 @@ proc parseGenericParamList(p: var TParser): PNode = var a = parseGenericParam(p) addSon(result, a) if p.tok.tokType notin {tkComma, tkSemiColon}: break + when defined(nimpretty2): + commaWasSemicolon(p.em) getTok(p) skipComment(p, a) optPar(p) From dba26656f6285bc0a0fc6767810981997dcdc68a Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sat, 16 Jun 2018 19:02:47 +0200 Subject: [PATCH 02/35] nimpretty: stuff that doesn't work --- compiler/layouter.nim | 40 ++++++++++++++++++++++++++++++++++------ 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/compiler/layouter.nim b/compiler/layouter.nim index 6a9a12d821..ec5d3d0884 100644 --- a/compiler/layouter.nim +++ b/compiler/layouter.nim @@ -29,7 +29,8 @@ type fid: FileIndex lastTok: TTokType inquote: bool - col, lastLineNumber, lineSpan, indentLevel: int + col, lastLineNumber, lineSpan, indentLevel, indWidth: int + lastIndent: int doIndentMore*: int content: string fixedUntil: int # marks where we must not go in the content @@ -69,15 +70,18 @@ template wr(x) = template goodCol(col): bool = col in 40..MaxLineLen -const splitters = {tkComma, tkSemicolon, tkParLe, tkParDotLe, - tkBracketLe, tkBracketLeColon, tkCurlyDotLe, - tkCurlyLe} +const + splitters = {tkComma, tkSemicolon, tkParLe, tkParDotLe, + tkBracketLe, tkBracketLeColon, tkCurlyDotLe, + tkCurlyLe} + sectionKeywords = {tkType, tkVar, tkConst, tkLet, tkUsing} template rememberSplit(kind) = if goodCol(em.col): em.altSplitPos[kind] = em.content.len -template moreIndent(em): int = (if em.doIndentMore > 0: 4 else: 2) +template moreIndent(em): int = + max(if em.doIndentMore > 0: em.indWidth*2 else: em.indWidth, 2) proc softLinebreak(em: var Emitter, lit: string) = # XXX Use an algorithm that is outlined here: @@ -115,6 +119,10 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) = wr lit var preventComment = false + if em.indWidth == 0 and tok.indent > 0: + # first indentation determines how many number of spaces to use: + em.indWidth = tok.indent + if tok.tokType == tkComment and tok.line == em.lastLineNumber and tok.indent >= 0: # we have an inline comment so handle it before the indentation token: emitComment(em, tok) @@ -129,9 +137,29 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) = wr("\L") for i in 2..tok.line - em.lastLineNumber: wr("\L") em.col = 0 - for i in 1..tok.indent: + #[ we only correct the indentation if it is slightly off, + so that code like + + const splitters = {tkComma, tkSemicolon, tkParLe, tkParDotLe, + tkBracketLe, tkBracketLeColon, tkCurlyDotLe, + tkCurlyLe} + + is not touched. + ]# + when false: + if tok.indent > em.lastIndent and em.indWidth > 1 and (tok.indent mod em.indWidth) == 1: + em.indentLevel = 0 + while em.indentLevel < tok.indent-1: + inc em.indentLevel, em.indWidth + when false: + if em.indWidth != 0 and + abs(tok.indent - em.nested*em.indWidth) <= em.indWidth and + em.lastTok notin sectionKeywords: + em.indentLevel = em.nested*em.indWidth + for i in 1..em.indentLevel: wr(" ") em.fixedUntil = em.content.high + em.lastIndent = tok.indent case tok.tokType of tokKeywordLow..tokKeywordHigh: From 8081a9b3d0e31d144d0aa7c4d954c905fa564a7a Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Fri, 4 May 2018 15:36:54 +0100 Subject: [PATCH 03/35] Exports unicode.toUpper/toLower in strutils module. --- lib/pure/strutils.nim | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index 5de013c26c..055e91faf8 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -17,6 +17,9 @@ import parseutils from math import pow, round, floor, log10 from algorithm import reverse +from unicode import toLower, toUpper +export toLower, toUpper + {.deadCodeElim: on.} # dce option deprecated {.push debugger:off .} # the user does not want to trace a part From 9e8623785577aff8ecffaea27f8cdc0d98c1edb1 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Fri, 4 May 2018 15:44:36 +0100 Subject: [PATCH 04/35] Adds smaller code samples from Chapters 1-3 to the tester. --- tests/niminaction/Chapter1/various1.nim | 45 +++ .../niminaction/Chapter2/explicit_discard.nim | 7 + tests/niminaction/Chapter2/no_def_eq.nim | 16 + tests/niminaction/Chapter2/no_iterator.nim | 7 + tests/niminaction/Chapter2/no_seq_type.nim | 6 + tests/niminaction/Chapter2/resultaccept.nim | 28 ++ tests/niminaction/Chapter2/resultreject.nim | 33 ++ tests/niminaction/Chapter2/various2.nim | 369 ++++++++++++++++++ tests/niminaction/Chapter3/various3.nim | 93 +++++ tests/niminaction/Chapter3/various3.nim.cfg | 1 + tests/testament/categories.nim | 38 +- 11 files changed, 642 insertions(+), 1 deletion(-) create mode 100644 tests/niminaction/Chapter1/various1.nim create mode 100644 tests/niminaction/Chapter2/explicit_discard.nim create mode 100644 tests/niminaction/Chapter2/no_def_eq.nim create mode 100644 tests/niminaction/Chapter2/no_iterator.nim create mode 100644 tests/niminaction/Chapter2/no_seq_type.nim create mode 100644 tests/niminaction/Chapter2/resultaccept.nim create mode 100644 tests/niminaction/Chapter2/resultreject.nim create mode 100644 tests/niminaction/Chapter2/various2.nim create mode 100644 tests/niminaction/Chapter3/various3.nim create mode 100644 tests/niminaction/Chapter3/various3.nim.cfg diff --git a/tests/niminaction/Chapter1/various1.nim b/tests/niminaction/Chapter1/various1.nim new file mode 100644 index 0000000000..688180fd2b --- /dev/null +++ b/tests/niminaction/Chapter1/various1.nim @@ -0,0 +1,45 @@ +discard """ + exitCode: 0 + outputsub: "Woof!" +""" + +import strutils +echo("hello".to_upper()) +echo("world".toUpper()) + +type + Dog = object #<1> + age: int #<2> + +let dog = Dog(age: 3) #<3> + +proc showNumber(num: int | float) = + echo(num) + +showNumber(3.14) +showNumber(42) + +for i in 0 .. <10: + echo(i) + +block: # Block added due to clash. + type + Dog = object + + proc bark(self: Dog) = #<1> + echo("Woof!") + + let dog = Dog() + dog.bark() #<2> + +import sequtils, future, strutils +let list = @["Dominik Picheta", "Andreas Rumpf", "Desmond Hume"] +list.map( + (x: string) -> (string, string) => (x.split[0], x.split[1]) +).echo + +import strutils +let list1 = @["Dominik Picheta", "Andreas Rumpf", "Desmond Hume"] +for name in list1: + echo((name.split[0], name.split[1])) + diff --git a/tests/niminaction/Chapter2/explicit_discard.nim b/tests/niminaction/Chapter2/explicit_discard.nim new file mode 100644 index 0000000000..3e94c335bd --- /dev/null +++ b/tests/niminaction/Chapter2/explicit_discard.nim @@ -0,0 +1,7 @@ +discard """ + line: 7 + errormsg: "has to be discarded" +""" + +proc myProc(name: string): string = "Hello " & name +myProc("Dominik") \ No newline at end of file diff --git a/tests/niminaction/Chapter2/no_def_eq.nim b/tests/niminaction/Chapter2/no_def_eq.nim new file mode 100644 index 0000000000..77f0a7dd8b --- /dev/null +++ b/tests/niminaction/Chapter2/no_def_eq.nim @@ -0,0 +1,16 @@ +discard """ + line: 16 + errormsg: "type mismatch" +""" + +type + Dog = object + name: string + + Cat = object + name: string + +let dog: Dog = Dog(name: "Fluffy") +let cat: Cat = Cat(name: "Fluffy") + +echo(dog == cat) \ No newline at end of file diff --git a/tests/niminaction/Chapter2/no_iterator.nim b/tests/niminaction/Chapter2/no_iterator.nim new file mode 100644 index 0000000000..331d69480a --- /dev/null +++ b/tests/niminaction/Chapter2/no_iterator.nim @@ -0,0 +1,7 @@ +discard """ + line: 6 + errormsg: "type mismatch" +""" + +for i in 5: + echo i \ No newline at end of file diff --git a/tests/niminaction/Chapter2/no_seq_type.nim b/tests/niminaction/Chapter2/no_seq_type.nim new file mode 100644 index 0000000000..493be270a7 --- /dev/null +++ b/tests/niminaction/Chapter2/no_seq_type.nim @@ -0,0 +1,6 @@ +discard """ + line: 6 + errormsg: "cannot infer the type of the sequence" +""" + +var list = @[] \ No newline at end of file diff --git a/tests/niminaction/Chapter2/resultaccept.nim b/tests/niminaction/Chapter2/resultaccept.nim new file mode 100644 index 0000000000..7dd976b40e --- /dev/null +++ b/tests/niminaction/Chapter2/resultaccept.nim @@ -0,0 +1,28 @@ +discard """ + output: "" +""" + +# Page 35. + +proc implicit: string = + "I will be returned" + +proc discarded: string = + discard "I will not be returned" + +proc explicit: string = + return "I will be returned" + +proc resultVar: string = + result = "I will be returned" + +proc resultVar2: string = + result = "" + result.add("I will be ") + result.add("returned") + +doAssert implicit() == "I will be returned" +doAssert discarded() == nil +doAssert explicit() == "I will be returned" +doAssert resultVar() == "I will be returned" +doAssert resultVar2() == "I will be returned" \ No newline at end of file diff --git a/tests/niminaction/Chapter2/resultreject.nim b/tests/niminaction/Chapter2/resultreject.nim new file mode 100644 index 0000000000..de59af7d95 --- /dev/null +++ b/tests/niminaction/Chapter2/resultreject.nim @@ -0,0 +1,33 @@ +discard """ + line: 27 + errormsg: "has to be discarded" +""" + +# Page 35. + +proc implicit: string = + "I will be returned" + +proc discarded: string = + discard "I will not be returned" + +proc explicit: string = + return "I will be returned" + +proc resultVar: string = + result = "I will be returned" + +proc resultVar2: string = + result = "" + result.add("I will be ") + result.add("returned") + +proc resultVar3: string = + result = "I am the result" + "I will cause an error" + +doAssert implicit() == "I will be returned" +doAssert discarded() == nil +doAssert explicit() == "I will be returned" +doAssert resultVar() == "I will be returned" +doAssert resultVar2() == "I will be returned" \ No newline at end of file diff --git a/tests/niminaction/Chapter2/various2.nim b/tests/niminaction/Chapter2/various2.nim new file mode 100644 index 0000000000..3f6a3f4537 --- /dev/null +++ b/tests/niminaction/Chapter2/various2.nim @@ -0,0 +1,369 @@ +discard """ + exitCode: 0 + outputsub: '''42 is greater than 0''' +""" + +if 42 >= 0: + echo "42 is greater than 0" + + +echo("Output: ", + 5) +echo(5 + + 5) +# --- Removed code that is supposed to fail here. Not going to test those. --- + +# Single-line comment +#[ +Multiline comment +]# +when false: + echo("Commented-out code") + +let decimal = 42 +let hex = 0x42 +let octal = 0o42 +let binary = 0b101010 + +let a: int16 = 42 +let b = 42'i8 + +let c = 1'f32 # --- Changed names here to avoid clashes --- +let d = 1.0e19 + +let e = false +let f = true + +let g = 'A' +let h = '\109' +let i = '\x79' + +let text = "The book title is \"Nim in Action\"" + +let filepath = "C:\\Program Files\\Nim" + +# --- Changed name here to avoid clashes --- +let filepath1 = r"C:\Program Files\Nim" + +let multiLine = """foo + bar + baz +""" +echo multiLine + +import strutils +# --- Changed name here to avoid clashes --- +let multiLine1 = """foo + bar + baz +""" +echo multiLine1.unindent +doAssert multiLine1.unindent == "foo\nbar\nbaz\n" + +proc fillString(): string = + result = "" + echo("Generating string") + for i in 0 .. 4: + result.add($i) #<1> + +const count = fillString() + +var + text1 = "hello" + number: int = 10 + isTrue = false + +var 火 = "Fire" +let ogień = true + +var `var` = "Hello" +echo(`var`) + +proc myProc(name: string): string = "Hello " & name +discard myProc("Dominik") + +proc bar(): int #<1> + +proc foo(): float = bar().float +proc bar(): int = foo().int + +proc noReturn() = echo("Hello") +proc noReturn2(): void = echo("Hello") + +proc noReturn3 = echo("Hello") + +proc message(recipient: string): auto = + "Hello " & recipient + +doAssert message("Dom") == "Hello Dom" + +proc max(a: int, b: int): int = + if a > b: a else: b + +doAssert max(5, 10) == 10 + +proc max2(a, b: int): int = + if a > b: a else: b + +proc genHello(name: string, surname = "Doe"): string = + "Hello " & name & " " & surname + +# -- Leaving these as asserts as that is in the original code, just in case +# -- somehow in the future `assert` is removed :) +assert genHello("Peter") == "Hello Peter Doe" +assert genHello("Peter", "Smith") == "Hello Peter Smith" + +proc genHello2(names: varargs[string]): string = + result = "" + for name in names: + result.add("Hello " & name & "\n") + +doAssert genHello2("John", "Bob") == "Hello John\nHello Bob\n" + +proc getUserCity(firstName, lastName: string): string = + case firstName + of "Damien": return "Tokyo" + of "Alex": return "New York" + else: return "Unknown" + +proc getUserCity(userID: int): string = + case userID + of 1: return "Tokyo" + of 2: return "New York" + else: return "Unknown" + +doAssert getUserCity("Damien", "Lundi") == "Tokyo" +doAssert getUserCity(2) == "New York" # -- Errata here: missing closing " + +import sequtils +let numbers = @[1, 2, 3, 4, 5, 6] +let odd = filter(numbers, proc (x: int): bool = x mod 2 != 0) +doAssert odd == @[1, 3, 5] + +import sequtils, future +let numbers1 = @[1, 2, 3, 4, 5, 6] +let odd1 = filter(numbers1, (x: int) -> bool => x mod 2 != 0) +assert odd1 == @[1, 3, 5] + +proc isValid(x: int, validator: proc (x: int): bool) = + if validator(x): echo(x, " is valid") + else: echo(x, " is NOT valid") + +import future +proc isValid2(x: int, validator: (x: int) -> bool) = + if validator(x): echo(x, " is valid") + else: echo(x, " is NOT valid") + +var list: array[3, int] +list[0] = 1 +list[1] = 42 +assert list[0] == 1 +assert list[1] == 42 +assert list[2] == 0 #<1> + +echo list.repr #<2> + +# echo list[500] + +var list2: array[-10 .. -9, int] +list2[-10] = 1 +list2[-9] = 2 + +var list3 = ["Hi", "There"] + +var list4 = ["My", "name", "is", "Dominik"] +for item in list4: + echo(item) + +for i in list4.low .. list4.high: + echo(list4[i]) + +var list5: seq[int] = @[] +doAssertRaises(IndexError): + list5[0] = 1 + +list5.add(1) + +assert list5[0] == 1 +doAssertRaises(IndexError): + echo list5[42] + +# -- Errata: var list: seq[int]; echo(list[0]). This now creates an exception, +# -- not a SIGSEGV. + +block: + var list = newSeq[string](3) + assert list[0] == nil + list[0] = "Foo" + list[1] = "Bar" + list[2] = "Baz" + + list.add("Lorem") + +block: + let list = @[4, 8, 15, 16, 23, 42] + for i in 0 .. 0 and age <= 10: + echo("You're still a child") +elif age > 10 and age < 18: + echo("You're a teenager") +else: + echo("You're an adult") + +let variable = "Arthur" +case variable +of "Arthur", "Zaphod", "Ford": + echo("Male") +of "Marvin": + echo("Robot") +of "Trillian": + echo("Female") +else: + echo("Unknown") + +let ageDesc = if age < 18: "Non-Adult" else: "Adult" + +block: + var i = 0 + while i < 3: + echo(i) + i.inc + +block label: + var i = 0 + while true: + while i < 5: + if i > 3: break label + i.inc + +iterator values(): int = + var i = 0 + while i < 5: + yield i + i.inc + +for value in values(): + echo(value) + +import os +for filename in walkFiles("*.nim"): + echo(filename) + +for item in @[1, 2, 3]: + echo(item) + +for i, value in @[1, 2, 3]: echo("Value at ", i, ": ", value) + +doAssertRaises(IOError): + proc second() = + raise newException(IOError, "Somebody set us up the bomb") + + proc first() = + second() + + first() + +block: + proc second() = + raise newException(IOError, "Somebody set us up the bomb") + + proc first() = + try: + second() + except: + echo("Cannot perform second action because: " & + getCurrentExceptionMsg()) + + first() + +block: + type + Person = object + name: string + age: int + + var person: Person + var person1 = Person(name: "Neo", age: 28) + +block: + type + PersonObj = object + name: string + age: int + PersonRef = ref PersonObj + + # proc setName(person: PersonObj) = + # person.name = "George" + + proc setName(person: PersonRef) = + person.name = "George" + +block: + type + Dog = object + name: string + + Cat = object + name: string + + let dog: Dog = Dog(name: "Fluffy") + let cat: Cat = Cat(name: "Fluffy") + +block: + type + Dog = tuple + name: string + + Cat = tuple + name: string + + let dog: Dog = (name: "Fluffy") + let cat: Cat = (name: "Fluffy") + + echo(dog == cat) + +block: + type + Point = tuple[x, y: int] + Point2 = (int, int) + + let pos: Point = (x: 100, y: 50) + doAssert pos == (100, 50) + + let pos1: Point = (x: 100, y: 50) + let (x, y) = pos1 #<1> + let (left, _) = pos1 + doAssert x == pos1[0] + doAssert y == pos1[1] + doAssert left == x + +block: + type + Color = enum + colRed, + colGreen, + colBlue + + let color: Color = colRed + +block: + type + Color {.pure.} = enum + red, green, blue + + let color = Color.red \ No newline at end of file diff --git a/tests/niminaction/Chapter3/various3.nim b/tests/niminaction/Chapter3/various3.nim new file mode 100644 index 0000000000..478229b00d --- /dev/null +++ b/tests/niminaction/Chapter3/various3.nim @@ -0,0 +1,93 @@ +import threadpool +proc foo: string = "Dog" +var x: FlowVar[string] = spawn foo() +assert(^x == "Dog") + +block: + type + Box = object + case empty: bool + of false: + contents: string + else: + discard + + var obj = Box(empty: false, contents: "Hello") + assert obj.contents == "Hello" + + var obj2 = Box(empty: true) + doAssertRaises(FieldError): + echo(obj2.contents) + +import json +assert parseJson("null").kind == JNull +assert parseJson("true").kind == JBool +assert parseJson("42").kind == JInt +assert parseJson("3.14").kind == JFloat +assert parseJson("\"Hi\"").kind == JString +assert parseJson("""{ "key": "value" }""").kind == JObject +assert parseJson("[1, 2, 3, 4]").kind == JArray + +import json +let data = """ + {"username": "Dominik"} +""" + +let obj = parseJson(data) +assert obj.kind == JObject +assert obj["username"].kind == JString +assert obj["username"].str == "Dominik" + +block: + proc count10(): int = + for i in 0 .. <10: + result.inc + assert count10() == 10 + +type + Point = tuple[x, y: int] + +var point = (5, 10) +var point2 = (x: 5, y: 10) + +type + Human = object + name: string + age: int + +var jeff = Human(name: "Jeff", age: 23) +var amy = Human(name: "Amy", age: 20) + +import asyncdispatch + +var future = newFuture[int]() +doAssert(not future.finished) + +future.callback = + proc (future: Future[int]) = + echo("Future is no longer empty, ", future.read) + +future.complete(42) + +import asyncdispatch, asyncfile + +when false: + var file = openAsync("") + let dataFut = file.readAll() + dataFut.callback = + proc (future: Future[string]) = + echo(future.read()) + + asyncdispatch.runForever() + +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") + +waitFor readFiles() + diff --git a/tests/niminaction/Chapter3/various3.nim.cfg b/tests/niminaction/Chapter3/various3.nim.cfg new file mode 100644 index 0000000000..6c1ded992e --- /dev/null +++ b/tests/niminaction/Chapter3/various3.nim.cfg @@ -0,0 +1 @@ +threads:on \ No newline at end of file diff --git a/tests/testament/categories.nim b/tests/testament/categories.nim index 84e536636a..f513090a47 100644 --- a/tests/testament/categories.nim +++ b/tests/testament/categories.nim @@ -266,8 +266,17 @@ proc testNimInAction(r: var TResults, cat: Category, options: string) = testSpec r, makeTest(filename, options, cat, actionCompile), targetCPP let tests = [ + "niminaction/Chapter1/various1", + "niminaction/Chapter2/various2", + "niminaction/Chapter2/resultaccept", + "niminaction/Chapter2/resultreject", + "niminaction/Chapter2/explicit_discard", + "niminaction/Chapter2/no_def_eq", + "niminaction/Chapter2/no_iterator", + "niminaction/Chapter2/no_seq_type", "niminaction/Chapter3/ChatApp/src/server", "niminaction/Chapter3/ChatApp/src/client", + "niminaction/Chapter3/various3", "niminaction/Chapter6/WikipediaStats/concurrency_regex", "niminaction/Chapter6/WikipediaStats/concurrency", "niminaction/Chapter6/WikipediaStats/naive", @@ -278,8 +287,34 @@ proc testNimInAction(r: var TResults, cat: Category, options: string) = "niminaction/Chapter7/Tweeter/src/tweeter", "niminaction/Chapter7/Tweeter/src/createDatabase", "niminaction/Chapter7/Tweeter/tests/database_test", - "niminaction/Chapter8/sdl/sdl_test", + "niminaction/Chapter8/sdl/sdl_test" ] + + # Verify that the files have not been modified. Death shall fall upon + # 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 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)) + + let refHashes = @[ + "51afdfa84b3ca3d810809d6c4e5037ba", "30f07e4cd5eaec981f67868d4e91cfcf", + "d14e7c032de36d219c9548066a97e846", "2e40bfd5daadb268268727da91bb4e81", + "c5d3853ed0aba04bf6d35ba28a98dca0", "058603145ff92d46c009006b06e5b228", + "7b94a029b94ddb7efafddd546c965ff6", "586d74514394e49f2370dfc01dd9e830", + "e1901837b757c9357dc8d259fd0ef0f6", "097670c7ae12e825debaf8ec3995227b", + "a8cb7b78cc78d28535ab467361db5d6e", "bfaec2816a1848991b530c1ad17a0184", + "47cb71bb4c1198d6d29cdbee05aa10b9", "87e4436809f9d73324cfc4f57f116770", + "7b7db5cddc8cf8fa9b6776eef1d0a31d", "e6e40219f0f2b877869b738737b7685e", + "6532ee87d819f2605a443d5e94f9422a", "9a8fe78c588d08018843b64b57409a02", + "03a801275b8b76b4170c870cd0da079d", "20bb7d3e2d38d43b0cb5fcff4909a4a8", + "af6844598f534fab6942abfa4dfe9ab2", "2a7a17f84f6503d9bc89a5ab8feea127" + ] + doAssert testHashes == refHashes, "Nim in Action tests were changed." + + # Run the tests. for testfile in tests: test "tests/" & testfile & ".nim", actionCompile @@ -291,6 +326,7 @@ proc testNimInAction(r: var TResults, cat: Category, options: string) = + # ------------------------- manyloc ------------------------------------------- #proc runSpecialTests(r: var TResults, options: string) = # for t in ["lib/packages/docutils/highlite"]: From c70706f4bd672487293b82fd5b1a0ddfbb3a6a78 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 17 Jun 2018 14:15:42 +0200 Subject: [PATCH 05/35] nimpretty: fixes pragma rendering --- compiler/parser.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/parser.nim b/compiler/parser.nim index 9a9fed0cfd..649f9d5066 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -922,7 +922,7 @@ proc parsePragma(p: var TParser): PNode = optPar(p) if p.tok.tokType in {tkCurlyDotRi, tkCurlyRi}: when defined(nimpretty2): - curlyRiWasPragma(p.em) + if p.tok.tokType == tkCurlyRi: curlyRiWasPragma(p.em) getTok(p) else: parMessage(p, "expected '.}'") From dbcdc4331a2d87aafa06ce49d50277474595064d Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 17 Jun 2018 14:16:01 +0200 Subject: [PATCH 06/35] testament: minor code formating change --- tests/testament/tester.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/testament/tester.nim b/tests/testament/tester.nim index 0185156ec4..0764b6363c 100644 --- a/tests/testament/tester.nim +++ b/tests/testament/tester.nim @@ -129,7 +129,7 @@ proc callCCompiler(cmdTemplate, filename, options: string, let c = parseCmdLine(cmdTemplate % ["target", targetToCmd[target], "options", options, "file", filename.quoteShell, "filedir", filename.getFileDir()]) - var p = startProcess(command="gcc", args=c[5.. ^1], + var p = startProcess(command="gcc", args=c[5 .. ^1], options={poStdErrToStdOut, poUsePath}) let outp = p.outputStream var x = newStringOfCap(120) From 98f3daea651b5e2f9ef620f6b9bb5a55e3530358 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 17 Jun 2018 14:25:23 +0200 Subject: [PATCH 07/35] nimpretty: don't touch dense binary operators --- compiler/layouter.nim | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/compiler/layouter.nim b/compiler/layouter.nim index ec5d3d0884..45ae8d305a 100644 --- a/compiler/layouter.nim +++ b/compiler/layouter.nim @@ -104,7 +104,7 @@ proc softLinebreak(em: var Emitter, lit: string) = proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) = template endsInWhite(em): bool = - em.content.len > 0 and em.content[em.content.high] in {' ', '\L'} + 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+{'_'} @@ -203,14 +203,18 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) = wr(TokTypeToStr[tok.tokType]) wr(" ") of tkOpr, tkDotDot: - if not em.endsInWhite: wr(" ") - wr(tok.ident.s) - template isUnary(tok): bool = - tok.strongSpaceB == 0 and tok.strongSpaceA > 0 + if tok.strongSpaceA == 0 and tok.strongSpaceB == 0: + # if not surrounded by whitespace, don't produce any whitespace either: + wr(tok.ident.s) + else: + if not em.endsInWhite: wr(" ") + wr(tok.ident.s) + template isUnary(tok): bool = + tok.strongSpaceB == 0 and tok.strongSpaceA > 0 - if not isUnary(tok) or em.lastTok in {tkOpr, tkDotDot}: - wr(" ") - rememberSplit(splitBinary) + if not isUnary(tok) or em.lastTok in {tkOpr, tkDotDot}: + wr(" ") + rememberSplit(splitBinary) of tkAccent: wr(TokTypeToStr[tok.tokType]) em.inquote = not em.inquote From 4616b28c7b592c8204d9f77f3c1f7db2fb7186d8 Mon Sep 17 00:00:00 2001 From: Kaushal Modi Date: Sun, 17 Jun 2018 12:37:41 -0400 Subject: [PATCH 08/35] Add support for italic and strikethrough ANSI escape codes (#8048) \e[3m -> italic \e[9m -> strikethrough On terminals not supporting italic text, the text is shown in reverse instead, and on terminals not supporting strikethrough text, the text is shown as is. --- lib/pure/terminal.nim | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/pure/terminal.nim b/lib/pure/terminal.nim index fcca4d5d74..8ee95957d6 100644 --- a/lib/pure/terminal.nim +++ b/lib/pure/terminal.nim @@ -467,16 +467,18 @@ proc resetAttributes*(f: File) = f.write(ansiResetCode) type - Style* = enum ## different styles for text output + Style* = enum ## different styles for text output styleBright = 1, ## bright text styleDim, ## dim text - styleUnknown, ## unknown + styleItalic, ## italic (or reverse on terminals not supporting) styleUnderscore = 4, ## underscored text styleBlink, ## blinking/bold text - styleReverse = 7, ## unknown + styleReverse = 7, ## reverse styleHidden ## hidden text + styleStrikethrough, ## strikethrough {.deprecated: [TStyle: Style].} +{.deprecated: [styleUnknown: styleItalic].} when not defined(windows): var @@ -843,6 +845,7 @@ when not defined(testing) and isMainModule: write(stdout, "never mind") stdout.eraseLine() stdout.styledWriteLine("styled text ", {styleBright, styleBlink, styleUnderscore}) + stdout.styledWriteLine("italic text ", {styleItalic}) stdout.setBackGroundColor(bgCyan, true) stdout.setForeGroundColor(fgBlue) stdout.writeLine("ordinary text") From 03b073d541e899585951fb51896ce4440f94387b Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Sat, 5 May 2018 21:14:48 +0100 Subject: [PATCH 09/35] Workaround VM bug in strutils --- lib/pure/strutils.nim | 5 +++-- tests/testament/categories.nim | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index 055e91faf8..ab34a0b2d0 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -17,8 +17,9 @@ import parseutils from math import pow, round, floor, log10 from algorithm import reverse -from unicode import toLower, toUpper -export toLower, toUpper +when defined(nimVmExportFixed): + from unicode import toLower, toUpper + export toLower, toUpper {.deadCodeElim: on.} # dce option deprecated diff --git a/tests/testament/categories.nim b/tests/testament/categories.nim index f513090a47..9affbc1599 100644 --- a/tests/testament/categories.nim +++ b/tests/testament/categories.nim @@ -293,13 +293,13 @@ proc testNimInAction(r: var TResults, cat: Category, options: string) = # Verify that the files have not been modified. Death shall fall upon # 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 so I can easily compile an errata later. + # 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)) - let refHashes = @[ + const refHashes = @[ "51afdfa84b3ca3d810809d6c4e5037ba", "30f07e4cd5eaec981f67868d4e91cfcf", "d14e7c032de36d219c9548066a97e846", "2e40bfd5daadb268268727da91bb4e81", "c5d3853ed0aba04bf6d35ba28a98dca0", "058603145ff92d46c009006b06e5b228", From 70664bd1a25cac6ce1a3d49ff82796153abbbc4f Mon Sep 17 00:00:00 2001 From: mboratko Date: Mon, 18 Jun 2018 02:05:35 -0400 Subject: [PATCH 10/35] Changed tutorial documentation referring to 'constant' to 'immutable' (#8056) * Changed tutorial documentation referring to 'constant' to 'immutable' * Clarification regarding side-effect free procedures fo:r multiple assignments --- doc/tut1.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/tut1.rst b/doc/tut1.rst index f2251c4637..aa6114cf78 100644 --- a/doc/tut1.rst +++ b/doc/tut1.rst @@ -208,7 +208,8 @@ Note that declaring multiple variables with a single assignment which calls a procedure can have unexpected results: the compiler will *unroll* the assignments and end up calling the procedure several times. If the result of the procedure depends on side effects, your variables may end up having -different values! For safety use only constant values. +different values! For safety use side-effect free procedures if making multiple +assignments. Constants @@ -642,7 +643,7 @@ initialisation. Parameters ---------- -Parameters are constant in the procedure body. By default, their value cannot be +Parameters are immutable in the procedure body. By default, their value cannot be changed because this allows the compiler to implement parameter passing in the most efficient way. If a mutable variable is needed inside the procedure, it has to be declared with ``var`` in the procedure body. Shadowing the parameter name From 3b5b3deecd8d49515dfa4836c452647c7ab7a506 Mon Sep 17 00:00:00 2001 From: Kaushal Modi Date: Fri, 15 Jun 2018 11:26:09 -0400 Subject: [PATCH 11/35] Add styledWrite macro Also: - Move the tests block to the end of the file - Fix the older tests - Add tests for existing styledEcho - Add new tests for styledWrite Fixes https://github.com/nim-lang/Nim/issues/8046. --- lib/pure/terminal.nim | 86 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 74 insertions(+), 12 deletions(-) diff --git a/lib/pure/terminal.nim b/lib/pure/terminal.nim index 8ee95957d6..3cdfe466de 100644 --- a/lib/pure/terminal.nim +++ b/lib/pure/terminal.nim @@ -692,6 +692,40 @@ template styledEchoProcessArg(f: File, cmd: TerminalCmd) = when cmd == bgColor: fgSetColor = false +macro styledWrite*(f: File, m: varargs[typed]): untyped = + ## Similar to ``write``, but treating terminal style arguments specially. + ## When some argument is ``Style``, ``set[Style]``, ``ForegroundColor``, + ## ``BackgroundColor`` or ``TerminalCmd`` then it is not sent directly to + ## ``f``, but instead corresponding terminal style proc is called. + ## + ## Example: + ## + ## .. code-block:: nim + ## + ## stdout.styledWrite(fgRed, "red text ") + ## stdout.styledWrite(fgGreen, "green text") + ## + let m = callsite() + var reset = false + result = newNimNode(nnkStmtList) + + for i in countup(2, m.len - 1): + let item = m[i] + case item.kind + of nnkStrLit..nnkTripleStrLit: + if i == m.len - 1: + # optimize if string literal is last, just call write + result.add(newCall(bindSym"write", f, item)) + if reset: result.add(newCall(bindSym"resetAttributes", f)) + return + else: + # if it is string literal just call write, do not enable reset + result.add(newCall(bindSym"write", f, item)) + else: + result.add(newCall(bindSym"styledEchoProcessArg", f, item)) + reset = true + if reset: result.add(newCall(bindSym"resetAttributes", f)) + macro styledWriteLine*(f: File, m: varargs[typed]): untyped = ## Similar to ``writeLine``, but treating terminal style arguments specially. ## When some argument is ``Style``, ``set[Style]``, ``ForegroundColor``, @@ -781,7 +815,7 @@ when defined(windows): inc i, x password.string.setLen(max(password.len - x, 0)) of chr(0x0): - # modifier key - ignore - for details see + # modifier key - ignore - for details see # https://github.com/nim-lang/Nim/issues/7764 continue else: @@ -840,17 +874,6 @@ proc resetAttributes*() {.noconv.} = ## ``system.addQuitProc(resetAttributes)``. resetAttributes(stdout) -when not defined(testing) and isMainModule: - #system.addQuitProc(resetAttributes) - write(stdout, "never mind") - stdout.eraseLine() - stdout.styledWriteLine("styled text ", {styleBright, styleBlink, styleUnderscore}) - stdout.styledWriteLine("italic text ", {styleItalic}) - stdout.setBackGroundColor(bgCyan, true) - stdout.setForeGroundColor(fgBlue) - stdout.writeLine("ordinary text") - stdout.resetAttributes() - proc isTrueColorSupported*(): bool = ## Returns true if a terminal supports true color. return trueColorIsSupported @@ -901,3 +924,42 @@ proc disableTrueColors*() = trueColorIsEnabled = false else: trueColorIsEnabled = false + +when not defined(testing) and isMainModule: + #system.addQuitProc(resetAttributes) + write(stdout, "never mind") + stdout.eraseLine() + stdout.styledWriteLine({styleBright, styleBlink, styleUnderscore}, "styled text ") + stdout.styledWriteLine("italic text ", {styleItalic}) + stdout.setBackGroundColor(bgCyan, true) + stdout.setForeGroundColor(fgBlue) + stdout.write("blue text in cyan background") + stdout.resetAttributes() + echo "" + stdout.writeLine("ordinary text") + echo "more ordinary text" + styledEcho styleBright, fgGreen, "[PASS]", resetStyle, fgGreen, " Yay!" + echo "ordinary text again" + styledEcho styleBright, fgRed, "[FAIL]", resetStyle, fgRed, " Nay :(" + echo "ordinary text again" + setForeGroundColor(fgGreen) + echo "green text" + echo "more green text" + setForeGroundColor(fgBlue) + echo "blue text" + resetAttributes() + echo "ordinary text" + + stdout.styledWriteLine(fgRed, "red text ") + # Below, resetStyle is needed to prevent leaking the set bgRed to the next + # newline. + stdout.styledWriteLine(fgWhite, bgRed, "white text in red background", resetStyle) + stdout.styledWriteLine(" ordinary text ") + stdout.styledWriteLine(fgGreen, "green text") + + stdout.styledWrite(fgRed, "red text ") + stdout.styledWrite(fgWhite, bgRed, "white text in red background") + stdout.styledWrite(" ordinary text ") + stdout.styledWrite(fgGreen, "green text") + echo "" + echo "ordinary text" From abbf9ba9f74fa2415efcad4d1792d68527eba09d Mon Sep 17 00:00:00 2001 From: Kaushal Modi Date: Fri, 15 Jun 2018 12:41:25 -0400 Subject: [PATCH 12/35] Convert styledWriteLine and styledEcho to templates This also fixes a bug in the styledWriteLine behavior where the background color leaked onto the next newline if that command did not end with resetStyle. Now it is not necessary to end styledWriteLine calls that set BackgroundColor to end in resetStyle. --- lib/pure/terminal.nim | 42 +++++++----------------------------------- 1 file changed, 7 insertions(+), 35 deletions(-) diff --git a/lib/pure/terminal.nim b/lib/pure/terminal.nim index 3cdfe466de..7ad243150a 100644 --- a/lib/pure/terminal.nim +++ b/lib/pure/terminal.nim @@ -726,11 +726,8 @@ macro styledWrite*(f: File, m: varargs[typed]): untyped = reset = true if reset: result.add(newCall(bindSym"resetAttributes", f)) -macro styledWriteLine*(f: File, m: varargs[typed]): untyped = - ## Similar to ``writeLine``, but treating terminal style arguments specially. - ## When some argument is ``Style``, ``set[Style]``, ``ForegroundColor``, - ## ``BackgroundColor`` or ``TerminalCmd`` then it is not sent directly to - ## ``f``, but instead corresponding terminal style proc is called. +template styledWriteLine*(f: File, args: varargs[untyped]) = + ## Calls ``styledWrite`` and appends a newline at the end. ## ## Example: ## @@ -739,35 +736,12 @@ macro styledWriteLine*(f: File, m: varargs[typed]): untyped = ## proc error(msg: string) = ## styledWriteLine(stderr, fgRed, "Error: ", resetStyle, msg) ## - let m = callsite() - var reset = false - result = newNimNode(nnkStmtList) + styledWrite(f, args) + write(f, "\n") - for i in countup(2, m.len - 1): - let item = m[i] - case item.kind - of nnkStrLit..nnkTripleStrLit: - if i == m.len - 1: - # optimize if string literal is last, just call writeLine - result.add(newCall(bindSym"writeLine", f, item)) - if reset: result.add(newCall(bindSym"resetAttributes", f)) - return - else: - # if it is string literal just call write, do not enable reset - result.add(newCall(bindSym"write", f, item)) - else: - result.add(newCall(bindSym"styledEchoProcessArg", f, item)) - reset = true - - result.add(newCall(bindSym"write", f, newStrLitNode("\n"))) - if reset: result.add(newCall(bindSym"resetAttributes", f)) - -macro styledEcho*(args: varargs[untyped]): untyped = +template styledEcho*(args: varargs[untyped]) = ## Echoes styles arguments to stdout using ``styledWriteLine``. - result = newCall(bindSym"styledWriteLine") - result.add(bindSym"stdout") - for arg in children(args): - result.add(arg) + stdout.styledWriteLine(args) proc getch*(): char = ## Read a single character from the terminal, blocking until it is entered. @@ -951,9 +925,7 @@ when not defined(testing) and isMainModule: echo "ordinary text" stdout.styledWriteLine(fgRed, "red text ") - # Below, resetStyle is needed to prevent leaking the set bgRed to the next - # newline. - stdout.styledWriteLine(fgWhite, bgRed, "white text in red background", resetStyle) + stdout.styledWriteLine(fgWhite, bgRed, "white text in red background") stdout.styledWriteLine(" ordinary text ") stdout.styledWriteLine(fgGreen, "green text") From dd81d9d5b711a9a78d5b4a26a62d02da2e495b21 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 19 Jun 2018 09:42:33 +0200 Subject: [PATCH 13/35] nimpretty improvements --- compiler/layouter.nim | 87 +++++++++++++++++++++++-------------------- compiler/lexer.nim | 14 ++++++- compiler/parser.nim | 2 +- 3 files changed, 60 insertions(+), 43 deletions(-) diff --git a/compiler/layouter.nim b/compiler/layouter.nim index 45ae8d305a..e07bde786f 100644 --- a/compiler/layouter.nim +++ b/compiler/layouter.nim @@ -7,10 +7,7 @@ # distribution, for details about the copyright. # -## Layouter for nimpretty. Still primitive but useful. -## TODO -## - Make indentations consistent. -## - Align 'if' and 'case' expressions properly. +## Layouter for nimpretty. import idents, lexer, lineinfos, llstream, options, msgs, strutils from os import changeFileExt @@ -30,14 +27,20 @@ type lastTok: TTokType inquote: bool col, lastLineNumber, lineSpan, indentLevel, indWidth: int - lastIndent: int + nested: int doIndentMore*: int content: string + indentStack: seq[int] fixedUntil: int # marks where we must not go in the content altSplitPos: array[SplitKind, int] # alternative split positions -proc openEmitter*(em: var Emitter, config: ConfigRef, fileIdx: FileIndex) = - let outfile = changeFileExt(config.toFullPath(fileIdx), ".pretty.nim") +proc openEmitter*(em: var Emitter, cache: IdentCache; + config: ConfigRef, fileIdx: FileIndex) = + let fullPath = config.toFullPath(fileIdx) + em.indWidth = getIndentWidth(fileIdx, llStreamOpen(fullPath, fmRead), + cache, config) + if em.indWidth == 0: em.indWidth = 2 + let outfile = changeFileExt(fullPath, ".pretty.nim") em.f = llStreamOpen(outfile, fmWrite) em.config = config em.fid = fileIdx @@ -45,6 +48,8 @@ proc openEmitter*(em: var Emitter, config: ConfigRef, fileIdx: FileIndex) = em.inquote = false em.col = 0 em.content = newStringOfCap(16_000) + em.indentStack = newSeqOfCap[int](30) + em.indentStack.add 0 if em.f == nil: rawMessage(config, errGenerated, "cannot open file: " & outfile) @@ -74,14 +79,15 @@ const splitters = {tkComma, tkSemicolon, tkParLe, tkParDotLe, tkBracketLe, tkBracketLeColon, tkCurlyDotLe, tkCurlyLe} - sectionKeywords = {tkType, tkVar, tkConst, tkLet, tkUsing} + 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 template moreIndent(em): int = - max(if em.doIndentMore > 0: em.indWidth*2 else: em.indWidth, 2) + (if em.doIndentMore > 0: em.indWidth*2 else: em.indWidth) proc softLinebreak(em: var Emitter, lit: string) = # XXX Use an algorithm that is outlined here: @@ -96,7 +102,8 @@ proc softLinebreak(em: var Emitter, lit: string) = # search backwards for a good split position: for a in em.altSplitPos: if a > em.fixedUntil: - let ws = "\L" & repeat(' ',em.indentLevel+moreIndent(em)) + let ws = "\L" & repeat(' ',em.indentLevel+moreIndent(em) - + ord(em.content[a] == ' ')) em.col = em.content.len - a em.content.insert(ws, a) break @@ -119,10 +126,6 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) = wr lit var preventComment = false - if em.indWidth == 0 and tok.indent > 0: - # first indentation determines how many number of spaces to use: - em.indWidth = tok.indent - if tok.tokType == tkComment and tok.line == em.lastLineNumber and tok.indent >= 0: # we have an inline comment so handle it before the indentation token: emitComment(em, tok) @@ -130,14 +133,17 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) = em.fixedUntil = em.content.high elif tok.indent >= 0: - em.indentLevel = tok.indent - # 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 - #[ we only correct the indentation if it is slightly off, + if em.lastTok in (splitters + oprSet): + em.indentLevel = tok.indent + else: + if tok.indent > em.indentStack[^1]: + em.indentStack.add tok.indent + else: + # dedent? + while em.indentStack.len > 1 and em.indentStack[^1] > tok.indent: + discard em.indentStack.pop() + em.indentLevel = em.indentStack.high * em.indWidth + #[ we only correct the indentation if it is not in an expression context, so that code like const splitters = {tkComma, tkSemicolon, tkParLe, tkParDotLe, @@ -146,20 +152,15 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) = is not touched. ]# - when false: - if tok.indent > em.lastIndent and em.indWidth > 1 and (tok.indent mod em.indWidth) == 1: - em.indentLevel = 0 - while em.indentLevel < tok.indent-1: - inc em.indentLevel, em.indWidth - when false: - if em.indWidth != 0 and - abs(tok.indent - em.nested*em.indWidth) <= em.indWidth and - em.lastTok notin sectionKeywords: - em.indentLevel = em.nested*em.indWidth + # 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 - em.lastIndent = tok.indent case tok.tokType of tokKeywordLow..tokKeywordHigh: @@ -168,15 +169,19 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) = elif not em.inquote and not endsInWhite(em): wr(" ") - wr(TokTypeToStr[tok.tokType]) + if not em.inquote: + wr(TokTypeToStr[tok.tokType]) - case tok.tokType - of tkAnd: rememberSplit(splitAnd) - of tkOr: rememberSplit(splitOr) - of tkIn, tkNotin: - rememberSplit(splitIn) - wr(" ") - else: discard + case tok.tokType + of tkAnd: rememberSplit(splitAnd) + of tkOr: rememberSplit(splitOr) + of tkIn, tkNotin: + rememberSplit(splitIn) + wr(" ") + else: discard + else: + # keywords in backticks are not normalized: + wr(tok.ident.s) of tkColon: wr(TokTypeToStr[tok.tokType]) diff --git a/compiler/lexer.nim b/compiler/lexer.nim index 13460f7c1f..c5afa6e97c 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -867,7 +867,7 @@ proc getOperator(L: var TLexer, tok: var TToken) = if buf[pos] in {CR, LF, nimlexbase.EndOfFile}: tok.strongSpaceB = -1 -proc newlineFollows*(L: var TLexer): bool = +proc newlineFollows*(L: TLexer): bool = var pos = L.bufpos var buf = L.buf while true: @@ -1220,3 +1220,15 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) = lexMessage(L, errGenerated, "invalid token: " & c & " (\\" & $(ord(c)) & ')') inc(L.bufpos) atTokenEnd() + +proc getIndentWidth*(fileIdx: FileIndex, inputstream: PLLStream; + cache: IdentCache; config: ConfigRef): int = + var lex: TLexer + var tok: TToken + initToken(tok) + openLexer(lex, fileIdx, inputstream, cache, config) + while true: + rawGetTok(lex, tok) + result = tok.indent + if result > 0 or tok.tokType == tkEof: break + closeLexer(lex) diff --git a/compiler/parser.nim b/compiler/parser.nim index 649f9d5066..f575f3d7ef 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -102,7 +102,7 @@ proc openParser*(p: var TParser, fileIdx: FileIndex, inputStream: PLLStream, initToken(p.tok) openLexer(p.lex, fileIdx, inputStream, cache, config) when defined(nimpretty2): - openEmitter(p.em, config, fileIdx) + openEmitter(p.em, cache, config, fileIdx) getTok(p) # read the first token p.firstTok = true p.strongSpaces = strongSpaces From 8508dc46c8c5b1535f42d2ade3687bb85d7037e7 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Tue, 19 Jun 2018 17:12:51 +0200 Subject: [PATCH 14/35] Fix minor codegen issue with static data types --- compiler/ccgexprs.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 82cc3a1fb0..0e8af5af5d 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -59,7 +59,7 @@ proc genLiteral(p: BProc, n: PNode, ty: PType): Rope = else: result = rope("NIM_NIL") of nkStrLit..nkTripleStrLit: - case skipTypes(ty, abstractVarRange).kind + case skipTypes(ty, abstractVarRange + {tyStatic}).kind of tyNil: result = genNilStringLiteral(p.module, n.info) of tyString: @@ -385,7 +385,7 @@ proc genDeepCopy(p: BProc; dest, src: TLoc) = else: addrLoc(p.config, a) - var ty = skipTypes(dest.t, abstractVarRange) + var ty = skipTypes(dest.t, abstractVarRange + {tyStatic}) case ty.kind of tyPtr, tyRef, tyProc, tyTuple, tyObject, tyArray: # XXX optimize this From db68bbe4f7e3ed2c6321e46d9b4d4977f1855a4e Mon Sep 17 00:00:00 2001 From: gemath <33119724+gemath@users.noreply.github.com> Date: Tue, 19 Jun 2018 17:13:33 +0000 Subject: [PATCH 15/35] Pegs AST read access (#8050) * Make PEG AST nodes readable from outside the module. * Added a test module for the pegs stdlib module. * Edited changelog. * Renamed ``sons`` iterator to ``items``, added ``pairs``, inlined both. * Updated entry and moved it to the right category. --- changelog.md | 3 ++ lib/pure/pegs.nim | 23 +++++++++++-- tests/stdlib/tpegs.nim | 78 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 102 insertions(+), 2 deletions(-) create mode 100644 tests/stdlib/tpegs.nim diff --git a/changelog.md b/changelog.md index 4067cb6939..4a490dfdde 100644 --- a/changelog.md +++ b/changelog.md @@ -107,6 +107,9 @@ use the Nim VM in a native Nim application. - Added the parameter ``val`` for the ``CritBitTree[T].incl`` proc. - The proc ``tgamma`` was renamed to ``gamma``. ``tgamma`` is deprecated. +- The ``pegs`` module now exports getters for the fields of its ``Peg`` and ``NonTerminal`` + object types. ``Peg``s with child nodes now have the standard ``items`` and ``pairs`` + iterators. ### Language additions diff --git a/lib/pure/pegs.nim b/lib/pure/pegs.nim index 39c5790ed8..d16527a561 100644 --- a/lib/pure/pegs.nim +++ b/lib/pure/pegs.nim @@ -32,7 +32,7 @@ const ## can be captured. More subpatterns cannot be captured! type - PegKind = enum + PegKind* = enum pkEmpty, pkAny, ## any character (.) pkAnyRune, ## any Unicode character (_) @@ -67,7 +67,7 @@ type pkRule, ## a <- b pkList, ## a, b pkStartAnchor ## ^ --> Internal DSL: startAnchor() - NonTerminalFlag = enum + NonTerminalFlag* = enum ntDeclared, ntUsed NonTerminalObj = object ## represents a non terminal symbol name: string ## the name of the symbol @@ -86,6 +86,25 @@ type else: sons: seq[Peg] NonTerminal* = ref NonTerminalObj +proc name*(nt: NonTerminal): string = nt.name +proc line*(nt: NonTerminal): int = nt.line +proc col*(nt: NonTerminal): int = nt.col +proc flags*(nt: NonTerminal): set[NonTerminalFlag] = nt.flags +proc rule*(nt: NonTerminal): Peg = nt.rule + +proc kind*(p: Peg): PegKind = p.kind +proc term*(p: Peg): string = p.term +proc ch*(p: Peg): char = p.ch +proc charChoice*(p: Peg): ref set[char] = p.charChoice +proc nt*(p: Peg): NonTerminal = p.nt +proc index*(p: Peg): range[0..MaxSubpatterns] = p.index +iterator items*(p: Peg): Peg {.inline.} = + for s in p.sons: + yield s +iterator pairs*(p: Peg): (int, Peg) {.inline.} = + for i in 0 ..< p.sons.len: + yield (i, p.sons[i]) + proc term*(t: string): Peg {.nosideEffect, rtl, extern: "npegs$1Str".} = ## constructs a PEG from a terminal string if t.len != 1: diff --git a/tests/stdlib/tpegs.nim b/tests/stdlib/tpegs.nim new file mode 100644 index 0000000000..e5b709a661 --- /dev/null +++ b/tests/stdlib/tpegs.nim @@ -0,0 +1,78 @@ +discard """ + output: ''' +pkNonTerminal: Sum @(2, 3) + pkSequence: (Product (('+' / '-') Product)*) + pkNonTerminal: Product @(3, 7) + pkSequence: (Value (('*' / '/') Value)*) + pkNonTerminal: Value @(4, 5) + pkOrderedChoice: (([0-9] [0-9]*) / ('(' Expr ')')) + pkSequence: ([0-9] [0-9]*) + pkCharChoice: [0-9] + pkGreedyRepSet: [0-9]* + pkSequence: ('(' Expr ')') + pkChar: '(' + pkNonTerminal: Expr @(1, 4) + pkNonTerminal: Sum @(2, 3) + pkChar: ')' + pkGreedyRep: (('*' / '/') Value)* + pkSequence: (('*' / '/') Value) + pkOrderedChoice: ('*' / '/') + pkChar: '*' + pkChar: '/' + pkNonTerminal: Value @(4, 5) + pkGreedyRep: (('+' / '-') Product)* + pkSequence: (('+' / '-') Product) + pkOrderedChoice: ('+' / '-') + pkChar: '+' + pkChar: '-' + pkNonTerminal: Product @(3, 7) +''' +""" + +import strutils, streams +import pegs + +const + indent = " " + +let + pegSrc = """ +Expr <- Sum +Sum <- Product (('+' / '-') Product)* +Product <- Value (('*' / '/') Value)* +Value <- [0-9]+ / '(' Expr ')' + """ + pegAst: Peg = pegSrc.peg + +var + outp = newStringStream() + processed: seq[string] = @[] + +proc prt(outp: Stream, kind: PegKind, s: string; level: int = 0) = + outp.writeLine indent.repeat(level) & "$1: $2" % [$kind, s] + +proc recLoop(p: Peg, level: int = 0) = + case p.kind + of pkEmpty..pkWhitespace: + discard + of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle: + outp.prt(p.kind, $p, level) + of pkChar, pkGreedyRepChar: + outp.prt(p.kind, $p, level) + of pkCharChoice, pkGreedyRepSet: + outp.prt(p.kind, $p, level) + of pkNonTerminal: + outp.prt(p.kind, + "$1 @($3, $4)" % [p.nt.name, $p.nt.rule.kind, $p.nt.line, $p.nt.col], level) + if not(p.nt.name in processed): + processed.add p.nt.name + p.nt.rule.recLoop level+1 + of pkBackRef..pkBackRefIgnoreStyle: + outp.prt(p.kind, $p, level) + else: + outp.prt(p.kind, $p, level) + for s in items(p): + s.recLoop level+1 + +pegAst.recLoop +echo outp.data \ No newline at end of file From 837d0c7270a67ea632d492586843807075eefb88 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 19 Jun 2018 20:06:04 +0200 Subject: [PATCH 16/35] nimpretty: proper command line handling; added tests; travis ensures these stay green --- .travis.yml | 2 + compiler/layouter.nim | 12 +- koch.nim | 8 +- {tools => nimpretty}/nimpretty.nim | 13 +- {tools => nimpretty}/nimpretty.nim.cfg | 0 nimpretty/tester.nim | 29 +++ nimpretty/tests/exhaustive.nim | 284 +++++++++++++++++++++++ nimpretty/tests/expected/exhaustive.nim | 291 ++++++++++++++++++++++++ 8 files changed, 622 insertions(+), 17 deletions(-) rename {tools => nimpretty}/nimpretty.nim (84%) rename {tools => nimpretty}/nimpretty.nim.cfg (100%) create mode 100644 nimpretty/tester.nim create mode 100644 nimpretty/tests/exhaustive.nim create mode 100644 nimpretty/tests/expected/exhaustive.nim diff --git a/.travis.yml b/.travis.yml index b7880cd366..5a091d0c70 100644 --- a/.travis.yml +++ b/.travis.yml @@ -45,6 +45,8 @@ script: - nimble install niminst - nim c --taintMode:on -d:nimCoroutines tests/testament/tester - tests/testament/tester --pedantic all -d:nimCoroutines + - nim c -o:bin/nimpretty nimpretty/nimpretty.nim + - nim c -r nimpretty/tester.nim - ./koch web - ./koch csource - ./koch nimsuggest diff --git a/compiler/layouter.nim b/compiler/layouter.nim index e07bde786f..eb591b6e19 100644 --- a/compiler/layouter.nim +++ b/compiler/layouter.nim @@ -21,7 +21,6 @@ type splitComma, splitParLe, splitAnd, splitOr, splitIn, splitBinary Emitter* = object - f: PLLStream config: ConfigRef fid: FileIndex lastTok: TTokType @@ -40,8 +39,6 @@ proc openEmitter*(em: var Emitter, cache: IdentCache; em.indWidth = getIndentWidth(fileIdx, llStreamOpen(fullPath, fmRead), cache, config) if em.indWidth == 0: em.indWidth = 2 - let outfile = changeFileExt(fullPath, ".pretty.nim") - em.f = llStreamOpen(outfile, fmWrite) em.config = config em.fid = fileIdx em.lastTok = tkInvalid @@ -50,12 +47,13 @@ proc openEmitter*(em: var Emitter, cache: IdentCache; em.content = newStringOfCap(16_000) em.indentStack = newSeqOfCap[int](30) em.indentStack.add 0 - if em.f == nil: - rawMessage(config, errGenerated, "cannot open file: " & outfile) proc closeEmitter*(em: var Emitter) = - em.f.llStreamWrite em.content - llStreamClose(em.f) + var f = llStreamOpen(em.config.outFile, fmWrite) + if f == nil: + rawMessage(em.config, errGenerated, "cannot open file: " & em.config.outFile) + f.llStreamWrite em.content + llStreamClose(f) proc countNewlines(s: string): int = result = 0 diff --git a/koch.nim b/koch.nim index 4f85c6583b..97e1da7761 100644 --- a/koch.nim +++ b/koch.nim @@ -254,15 +254,13 @@ proc buildTool(toolname, args: string) = copyFile(dest="bin" / splitFile(toolname).name.exe, source=toolname.exe) proc buildTools(latest: bool) = - let nimsugExe = "bin/nimsuggest".exe - nimexec "c --noNimblePath -p:compiler -d:release -o:" & nimsugExe & + nimexec "c --noNimblePath -p:compiler -d:release -o:" & ("bin/nimsuggest".exe) & " nimsuggest/nimsuggest.nim" - let nimgrepExe = "bin/nimgrep".exe - nimexec "c -d:release -o:" & nimgrepExe & " tools/nimgrep.nim" + nimexec "c -d:release -o:" & ("bin/nimgrep".exe) & " tools/nimgrep.nim" when defined(windows): buildVccTool() - #nimexec "c -o:" & ("bin/nimresolve".exe) & " tools/nimresolve.nim" + nimexec "c -o:" & ("bin/nimpretty".exe) & " nimpretty/nimpretty.nim" buildNimble(latest) diff --git a/tools/nimpretty.nim b/nimpretty/nimpretty.nim similarity index 84% rename from tools/nimpretty.nim rename to nimpretty/nimpretty.nim index 89e6ef9052..aa9756c457 100644 --- a/tools/nimpretty.nim +++ b/nimpretty/nimpretty.nim @@ -25,6 +25,7 @@ Usage: nimpretty [options] file.nim Options: --backup:on|off create a backup file before overwritting (default: ON) + --output:file set the output file (default: overwrite the .nim file) --version show the version --help show this help """ @@ -39,18 +40,18 @@ proc writeVersion() = stdout.flushFile() quit(0) -proc prettyPrint(infile: string) = - let conf = newConfigRef() +proc prettyPrint(infile, outfile: string) = + var conf = newConfigRef() let fileIdx = fileInfoIdx(conf, infile) + conf.outFile = outfile when defined(nimpretty2): discard parseFile(fileIdx, newIdentCache(), conf) else: let tree = parseFile(fileIdx, newIdentCache(), conf) - let outfile = changeFileExt(infile, ".pretty.nim") renderModule(tree, infile, outfile, {}, fileIdx, conf) proc main = - var infile: string + var infile, outfile: string var backup = true for kind, key, val in getopt(): case kind @@ -61,12 +62,14 @@ proc main = of "help", "h": writeHelp() of "version", "v": writeVersion() of "backup": backup = parseBool(val) + of "output", "o": outfile = val else: writeHelp() of cmdEnd: assert(false) # cannot happen if infile.len == 0: quit "[Error] no input file." if backup: os.copyFile(source=infile, dest=changeFileExt(infile, ".nim.backup")) - prettyPrint(infile) + if outfile.len == 0: outfile = infile + prettyPrint(infile, outfile) main() diff --git a/tools/nimpretty.nim.cfg b/nimpretty/nimpretty.nim.cfg similarity index 100% rename from tools/nimpretty.nim.cfg rename to nimpretty/nimpretty.nim.cfg diff --git a/nimpretty/tester.nim b/nimpretty/tester.nim new file mode 100644 index 0000000000..7db245b5f3 --- /dev/null +++ b/nimpretty/tester.nim @@ -0,0 +1,29 @@ +# Small program that runs the test cases + +import strutils, os + +const + dir = "nimpretty/tests/" + +var + failures = 0 + +proc test(infile, outfile: string) = + if execShellCmd("nimpretty -o:$2 --backup:off $1" % [infile, outfile]) != 0: + quit("FAILURE") + let nimFile = splitFile(infile).name + let expected = dir / "expected" / nimFile & ".nim" + let produced = dir / nimFile & ".pretty" + if strip(readFile(expected)) != strip(readFile(produced)): + echo "FAILURE: files differ: ", nimFile + discard execShellCmd("diff -uNdr " & produced & " " & expected) + failures += 1 + else: + echo "SUCCESS: files identical: ", nimFile + +for t in walkFiles(dir / "*.nim"): + let res = t.changeFileExt("pretty") + test(t, res) + removeFile(res) + +if failures > 0: quit($failures & " failures occurred.") diff --git a/nimpretty/tests/exhaustive.nim b/nimpretty/tests/exhaustive.nim new file mode 100644 index 0000000000..5d37b08828 --- /dev/null +++ b/nimpretty/tests/exhaustive.nim @@ -0,0 +1,284 @@ +discard """ + outputsub: '''ObjectAssignmentError''' + exitcode: "1" +""" + +import verylongnamehere,verylongnamehere,verylongnamehereverylongnamehereverylong,namehere,verylongnamehere + +type + GeneralTokenizer* = object of RootObj ## comment here + kind*: TokenClass ## and here + start*, length*: int ## you know how it goes... + buf: cstring + pos: int # other comment here. + state: TokenClass + +var x*: string +var y: seq[string] #[ yay inline comments. So nice I have to care bout these. ]# + +echo "#", x, "##", y, "#" + +echo (tup, here) +echo(argA, argB) + +import macros + +## A documentation comment here. +## That spans multiple lines. +## And is not to be touched. + +const numbers = [4u8, 5'u16, 89898_00] + +macro m(n): untyped = + result = foo"string literal" + +{.push m.} +proc p() = echo "p", 1+4 * 5, if true: 5 else: 6 +proc q(param: var ref ptr string) = + p() + if true: + echo a and b or not c and not -d +{.pop.} + +q() + +when false: + # bug #4766 + type + Plain = ref object + discard + + Wrapped[T] = object + value: T + + converter toWrapped[T](value: T): Wrapped[T] = + Wrapped[T](value: value) + + let result = Plain() + discard $result + +when false: + # bug #3670 + template someTempl(someConst: bool) = + when someConst: + var a: int + if true: + when not someConst: + var a: int + a = 5 + + someTempl(true) + + +# +# +# The Nim Compiler +# (c) Copyright 2018 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## Layouter for nimpretty. Still primitive but useful. + +import idents, lexer, lineinfos, llstream, options, msgs, strutils +from os import changeFileExt + +const + MaxLineLen = 80 + LineCommentColumn = 30 + +type + SplitKind = enum + splitComma, splitParLe, splitAnd, splitOr, splitIn, splitBinary + + Emitter* = object + f: PLLStream + config: ConfigRef + fid: FileIndex + lastTok: TTokType + inquote {.pragmaHereWrongCurlyEnd}: bool + col, lastLineNumber, lineSpan, indentLevel: int + content: string + 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, fileIdx: FileIndex) {.pragmaHereWrongCurlyEnd} = + let outfile = changeFileExt(config.toFullPath(fileIdx), ".pretty.nim") + em.f = llStreamOpen(outfile, fmWrite) + em.config = config + em.fid = fileIdx + em.lastTok = tkInvalid + em.inquote = false + em.col = 0 + em.content = newStringOfCap(16_000) + if em.f == nil: + rawMessage(config, errGenerated, "cannot open file: " & outfile) + +proc closeEmitter*(em: var Emitter) {.inline.} = + em.f.llStreamWrite em.content + llStreamClose(em.f) + +proc countNewlines(s: string): int = + result = 0 + for i in 0..= 0 and s[i] != '\L': + dec i + inc em.col + +template wr(x) = + em.content.add x + inc em.col, x.len + +template goodCol(col): bool = col in 40..MaxLineLen + +const splitters = {tkComma, tkSemicolon, tkParLe, tkParDotLe, + tkBracketLe, tkBracketLeColon, tkCurlyDotLe, + tkCurlyLe} + +template rememberSplit(kind) = + if goodCol(em.col): + em.altSplitPos[kind] = em.content.len + +proc softLinebreak(em: var Emitter, lit: string) = + # XXX Use an algorithm that is outlined here: + # https://llvm.org/devmtg/2013-04/jasper-slides.pdf + # +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: + wr("\L") + em.col = 0 + for i in 1..em.indentLevel+2: wr(" ") + else: + # search backwards for a good split position: + for a in em.altSplitPos: + if a > em.fixedUntil: + let ws = "\L" & repeat(' ',em.indentLevel+2) + em.col = em.content.len - a + em.content.insert(ws, a) + break + +proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) = + + template endsInWhite(em): bool = + em.content.len > 0 and em.content[em.content.high] in {' ', '\L'} + template endsInAlpha(em): bool = + em.content.len > 0 and em.content[em.content.high] in SymChars+{'_'} + + 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) + 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 + + var preventComment = case tok.tokType + of tokKeywordLow..tokKeywordHigh: + if endsInAlpha(em): wr(" ") + wr(TokTypeToStr[tok.tokType]) + + case tok.tokType + of tkAnd: rememberSplit(splitAnd) + of tkOr: rememberSplit(splitOr) + of tkIn: rememberSplit(splitIn) + else: 90 + else: + "case returns value" + + + if tok.tokType == tkComment and tok.line == em.lastLineNumber and tok.indent >= 0: + # we have an inline comment so handle it before the indentation token: + emitComment(em, tok) + preventComment = true + em.fixedUntil = em.content.high + + elif tok.indent >= 0: + em.indentLevel = tok.indent + # 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..tok.indent: + wr(" ") + em.fixedUntil = em.content.high + + case tok.tokType + of tokKeywordLow..tokKeywordHigh: + if endsInAlpha(em): wr(" ") + wr(TokTypeToStr[tok.tokType]) + + case tok.tokType + of tkAnd: rememberSplit(splitAnd) + of tkOr: rememberSplit(splitOr) + of tkIn: rememberSplit(splitIn) + else: discard + + of tkColon: + wr(TokTypeToStr[tok.tokType]) + wr(" ") + of tkSemicolon, + tkComma: + wr(TokTypeToStr[tok.tokType]) + wr(" ") + rememberSplit(splitComma) + of tkParLe, tkParRi, tkBracketLe, + tkBracketRi, tkCurlyLe, tkCurlyRi, + tkBracketDotLe, tkBracketDotRi, + tkCurlyDotLe, tkCurlyDotRi, + tkParDotLe, tkParDotRi, + tkColonColon, tkDot, tkBracketLeColon: + wr(TokTypeToStr[tok.tokType]) + if tok.tokType in splitters: + rememberSplit(splitParLe) + of tkEquals: + if not em.endsInWhite: wr(" ") + wr(TokTypeToStr[tok.tokType]) + wr(" ") + of tkOpr, tkDotDot: + if not em.endsInWhite: wr(" ") + wr(tok.ident.s) + template isUnary(tok): bool = + tok.strongSpaceB == 0 and tok.strongSpaceA > 0 + + if not isUnary(tok) or em.lastTok in {tkOpr, tkDotDot}: + wr(" ") + rememberSplit(splitBinary) + of tkAccent: + wr(TokTypeToStr[tok.tokType]) + em.inquote = not em.inquote + of tkComment: + if not preventComment: + emitComment(em, tok) + 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(" ") + em.lineSpan = countNewlines(lit) + if em.lineSpan > 0: calcCol(em, lit) + wr lit + 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 + + em.lastTok = tok.tokType + em.lastLineNumber = tok.line + em.lineSpan + em.lineSpan = 0 + +proc starWasExportMarker*(em: var Emitter) = + if em.content.endsWith(" * "): + setLen(em.content, em.content.len-3) + em.content.add("*") + dec em.col, 2 diff --git a/nimpretty/tests/expected/exhaustive.nim b/nimpretty/tests/expected/exhaustive.nim new file mode 100644 index 0000000000..b8c55033ce --- /dev/null +++ b/nimpretty/tests/expected/exhaustive.nim @@ -0,0 +1,291 @@ +discard """ + outputsub: '''ObjectAssignmentError''' + exitcode: "1" +""" + +import verylongnamehere, verylongnamehere, + verylongnamehereverylongnamehereverylong, namehere, verylongnamehere + +type + GeneralTokenizer* = object of RootObj ## comment here + kind*: TokenClass ## and here + start*, length*: int ## you know how it goes... + buf: cstring + pos: int # other comment here. + state: TokenClass + +var x*: string +var y: seq[string] #[ yay inline comments. So nice I have to care bout these. ]# + +echo "#", x, "##", y, "#" + +echo (tup, here) +echo(argA, argB) + +import macros + +## A documentation comment here. +## That spans multiple lines. +## And is not to be touched. + +const numbers = [4u8, 5'u16, 89898_00] + +macro m(n): untyped = + result = foo"string literal" + +{.push m.} +proc p() = echo "p", 1+4 * 5, if true: 5 else: 6 +proc q(param: var ref ptr string) = + p() + if true: + echo a and b or not c and not -d +{.pop.} + +q() + +when false: + # bug #4766 + type + Plain = ref object + discard + + Wrapped[T] = object + value: T + + converter toWrapped[T](value: T): Wrapped[T] = + Wrapped[T](value: value) + + let result = Plain() + discard $result + +when false: + # bug #3670 + template someTempl(someConst: bool) = + when someConst: + var a: int + if true: + when not someConst: + var a: int + a = 5 + + someTempl(true) + + +# +# +# The Nim Compiler +# (c) Copyright 2018 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## Layouter for nimpretty. Still primitive but useful. + +import idents, lexer, lineinfos, llstream, options, msgs, strutils +from os import changeFileExt + +const + MaxLineLen = 80 + LineCommentColumn = 30 + +type + SplitKind = enum + splitComma, splitParLe, splitAnd, splitOr, splitIn, splitBinary + + Emitter* = object + f: PLLStream + config: ConfigRef + fid: FileIndex + lastTok: TTokType + inquote {.pragmaHereWrongCurlyEnd.}: bool + col, lastLineNumber, lineSpan, indentLevel: int + content: string + 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; + fileIdx: FileIndex) {.pragmaHereWrongCurlyEnd.} = + let outfile = changeFileExt(config.toFullPath(fileIdx), ".pretty.nim") + em.f = llStreamOpen(outfile, fmWrite) + em.config = config + em.fid = fileIdx + em.lastTok = tkInvalid + em.inquote = false + em.col = 0 + em.content = newStringOfCap(16_000) + if em.f == nil: + rawMessage(config, errGenerated, "cannot open file: " & outfile) + +proc closeEmitter*(em: var Emitter) {.inline.} = + em.f.llStreamWrite em.content + llStreamClose(em.f) + +proc countNewlines(s: string): int = + result = 0 + for i in 0..= 0 and s[i] != '\L': + dec i + inc em.col + +template wr(x) = + em.content.add x + inc em.col, x.len + +template goodCol(col): bool = col in 40..MaxLineLen + +const splitters = {tkComma, tkSemicolon, tkParLe, tkParDotLe, + tkBracketLe, tkBracketLeColon, tkCurlyDotLe, + tkCurlyLe} + +template rememberSplit(kind) = + if goodCol(em.col): + em.altSplitPos[kind] = em.content.len + +proc softLinebreak(em: var Emitter; lit: string) = + # XXX Use an algorithm that is outlined here: + # https://llvm.org/devmtg/2013-04/jasper-slides.pdf + # +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: + wr("\L") + em.col = 0 + for i in 1..em.indentLevel+2: wr(" ") + else: + # search backwards for a good split position: + for a in em.altSplitPos: + if a > em.fixedUntil: + let ws = "\L" & repeat(' ', em.indentLevel+2) + em.col = em.content.len - a + em.content.insert(ws, a) + break + +proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) = + + template endsInWhite(em): bool = + em.content.len > 0 and em.content[em.content.high] in {' ', '\L'} + template endsInAlpha(em): bool = + em.content.len > 0 and em.content[em.content.high] in SymChars+{'_'} + + 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) + 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 + + var preventComment = case tok.tokType + of tokKeywordLow..tokKeywordHigh: + if endsInAlpha(em): wr(" ") + wr(TokTypeToStr[tok.tokType]) + + case tok.tokType + of tkAnd: rememberSplit(splitAnd) + of tkOr: rememberSplit(splitOr) + of tkIn: rememberSplit(splitIn) + else: 90 + else: + "case returns value" + + + if tok.tokType == tkComment and tok.line == em.lastLineNumber and + tok.indent >= 0: + # we have an inline comment so handle it before the indentation token: + emitComment(em, tok) + preventComment = true + em.fixedUntil = em.content.high + + elif tok.indent >= 0: + em.indentLevel = tok.indent + # 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..tok.indent: + wr(" ") + em.fixedUntil = em.content.high + + case tok.tokType + of tokKeywordLow..tokKeywordHigh: + if endsInAlpha(em): wr(" ") + wr(TokTypeToStr[tok.tokType]) + + case tok.tokType + of tkAnd: rememberSplit(splitAnd) + of tkOr: rememberSplit(splitOr) + of tkIn: rememberSplit(splitIn) + else: discard + + of tkColon: + wr(TokTypeToStr[tok.tokType]) + wr(" ") + of tkSemicolon, + tkComma: + wr(TokTypeToStr[tok.tokType]) + wr(" ") + rememberSplit(splitComma) + of tkParLe, tkParRi, tkBracketLe, + tkBracketRi, tkCurlyLe, tkCurlyRi, + tkBracketDotLe, tkBracketDotRi, + tkCurlyDotLe, tkCurlyDotRi, + tkParDotLe, tkParDotRi, + tkColonColon, tkDot, tkBracketLeColon: + wr(TokTypeToStr[tok.tokType]) + if tok.tokType in splitters: + rememberSplit(splitParLe) + of tkEquals: + if not em.endsInWhite: wr(" ") + wr(TokTypeToStr[tok.tokType]) + wr(" ") + of tkOpr, tkDotDot: + if not em.endsInWhite: wr(" ") + wr(tok.ident.s) + template isUnary(tok): bool = + tok.strongSpaceB == 0 and tok.strongSpaceA > 0 + + if not isUnary(tok) or em.lastTok in {tkOpr, tkDotDot}: + wr(" ") + rememberSplit(splitBinary) + of tkAccent: + wr(TokTypeToStr[tok.tokType]) + em.inquote = not em.inquote + of tkComment: + if not preventComment: + emitComment(em, tok) + 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( + " ") + em.lineSpan = countNewlines(lit) + if em.lineSpan > 0: calcCol(em, lit) + wr lit + 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 + + em.lastTok = tok.tokType + em.lastLineNumber = tok.line + em.lineSpan + em.lineSpan = 0 + +proc starWasExportMarker*(em: var Emitter) = + if em.content.endsWith(" * "): + setLen(em.content, em.content.len-3) + em.content.add("*") + dec em.col, 2 From fb62dd1fae7a405d80f0d4257ddb1f4d48e204f0 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Mon, 18 Jun 2018 14:30:18 +0200 Subject: [PATCH 17/35] Fix constant folding for shl/not Since the source and destination types are the same the result should be trimmed to fit. --- compiler/semfold.nim | 11 ++++++-- tests/arithm/tnot.nim | 58 +++++++++++++++++++++++++++++++++++++++++++ tests/arithm/tshl.nim | 34 +++++++++++++++++++++++++ 3 files changed, 101 insertions(+), 2 deletions(-) create mode 100644 tests/arithm/tnot.nim create mode 100644 tests/arithm/tshl.nim diff --git a/compiler/semfold.nim b/compiler/semfold.nim index 10a223ea2b..eceb104700 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -211,7 +211,12 @@ proc evalOp(m: TMagic, n, a, b, c: PNode; g: ModuleGraph): PNode = of mUnaryMinusF64: result = newFloatNodeT(- getFloat(a), n, g) of mNot: result = newIntNodeT(1 - getInt(a), n, g) of mCard: result = newIntNodeT(nimsets.cardSet(g.config, a), n, g) - of mBitnotI: result = newIntNodeT(not getInt(a), n, g) + of mBitnotI: + case skipTypes(n.typ, abstractRange).kind + of tyUInt..tyUInt64: + result = newIntNodeT((not getInt(a)) and lastOrd(g.config, a.typ, fixedUnsigned=true), n, g) + else: + result = newIntNodeT(not getInt(a), n, g) of mLengthArray: result = newIntNodeT(lengthOrd(g.config, a.typ), n, g) of mLengthSeq, mLengthOpenArray, mXLenSeq, mLengthStr, mXLenStr: if a.kind == nkNilLit: @@ -250,8 +255,10 @@ proc evalOp(m: TMagic, n, a, b, c: PNode; g: ModuleGraph): PNode = of tyInt8: result = newIntNodeT(int8(getInt(a)) shl int8(getInt(b)), n, g) of tyInt16: result = newIntNodeT(int16(getInt(a)) shl int16(getInt(b)), n, g) of tyInt32: result = newIntNodeT(int32(getInt(a)) shl int32(getInt(b)), n, g) - of tyInt64, tyInt, tyUInt..tyUInt64: + of tyInt64, tyInt: result = newIntNodeT(`shl`(getInt(a), getInt(b)), n, g) + of tyUInt..tyUInt64: + result = newIntNodeT(`shl`(getInt(a), getInt(b)) and lastOrd(g.config, a.typ, fixedUnsigned=true), n, g) else: internalError(g.config, n.info, "constant folding for shl") of mShrI: case skipTypes(n.typ, abstractRange).kind diff --git a/tests/arithm/tnot.nim b/tests/arithm/tnot.nim new file mode 100644 index 0000000000..6a4877b2c6 --- /dev/null +++ b/tests/arithm/tnot.nim @@ -0,0 +1,58 @@ +discard """ + output: ''' +-5 +-5 +-5 +-5 +4 +4 +4 +4 +251 +65531 +4294967291 +18446744073709551611 +4 +4 +4 +4 +''' +""" + +# Signed types +block: + const t0: int8 = not 4 + const t1: int16 = not 4 + const t2: int32 = not 4 + const t3: int64 = not 4 + const t4: int8 = not -5 + const t5: int16 = not -5 + const t6: int32 = not -5 + const t7: int64 = not -5 + echo t0 + echo t1 + echo t2 + echo t3 + echo t4 + echo t5 + echo t6 + echo t7 + +# Unsigned types +block: + const t0: uint8 = not 4'u8 + const t1: uint16 = not 4'u16 + const t2: uint32 = not 4'u32 + const t3: uint64 = not 4'u64 + const t4: uint8 = not 251'u8 + const t5: uint16 = not 65531'u16 + const t6: uint32 = not 4294967291'u32 + const t7: uint64 = not 18446744073709551611'u64 + echo t0 + echo t1 + echo t2 + echo t3 + echo t4 + echo t5 + echo t6 + echo t7 diff --git a/tests/arithm/tshl.nim b/tests/arithm/tshl.nim new file mode 100644 index 0000000000..0aa46d021c --- /dev/null +++ b/tests/arithm/tshl.nim @@ -0,0 +1,34 @@ +discard """ + output: ''' +0 +0 +1 +1 +0 +0 +0 +1 +''' +""" + +# Signed types +block: + const t0: int8 = 1'i8 shl 8 + const t1: int16 = 1'i16 shl 16 + const t2: int32 = 1'i32 shl 32 + const t3: int64 = 1'i64 shl 64 + echo t0 + echo t1 + echo t2 + echo t3 + +# Unsigned types +block: + const t0: uint8 = 1'u8 shl 8 + const t1: uint16 = 1'u16 shl 16 + const t2: uint32 = 1'u32 shl 32 + const t3: uint64 = 1'u64 shl 64 + echo t0 + echo t1 + echo t2 + echo t3 From c3090fcb48b0323cb7be79d2fcd009e11360dab5 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 19 Jun 2018 23:45:11 +0200 Subject: [PATCH 18/35] nimpretty: don't produce trailing whitespace; fixes the rendering of unary operators --- compiler/layouter.nim | 13 +++++++++---- nimpretty/tests/exhaustive.nim | 2 +- nimpretty/tests/expected/exhaustive.nim | 12 ++++++------ 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/compiler/layouter.nim b/compiler/layouter.nim index eb591b6e19..d0c9a77b93 100644 --- a/compiler/layouter.nim +++ b/compiler/layouter.nim @@ -93,6 +93,8 @@ 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(" ") @@ -100,8 +102,11 @@ proc softLinebreak(em: var Emitter, lit: string) = # search backwards for a good split position: for a in em.altSplitPos: if a > em.fixedUntil: - let ws = "\L" & repeat(' ',em.indentLevel+moreIndent(em) - - ord(em.content[a] == ' ')) + var spaces = 0 + while a+spaces < em.content.len and em.content[a+spaces] == ' ': + inc spaces + if spaces > 0: delete(em.content, a, a+spaces-1) + let ws = "\L" & repeat(' ',em.indentLevel+moreIndent(em)) em.col = em.content.len - a em.content.insert(ws, a) break @@ -186,8 +191,8 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) = wr(" ") of tkSemicolon, tkComma: wr(TokTypeToStr[tok.tokType]) - wr(" ") rememberSplit(splitComma) + wr(" ") of tkParDotLe, tkParLe, tkBracketDotLe, tkBracketLe, tkCurlyLe, tkCurlyDotLe, tkBracketLeColon: if tok.strongSpaceA > 0 and not em.endsInWhite: @@ -215,7 +220,7 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) = template isUnary(tok): bool = tok.strongSpaceB == 0 and tok.strongSpaceA > 0 - if not isUnary(tok) or em.lastTok in {tkOpr, tkDotDot}: + if not isUnary(tok): wr(" ") rememberSplit(splitBinary) of tkAccent: diff --git a/nimpretty/tests/exhaustive.nim b/nimpretty/tests/exhaustive.nim index 5d37b08828..1039e308bd 100644 --- a/nimpretty/tests/exhaustive.nim +++ b/nimpretty/tests/exhaustive.nim @@ -16,7 +16,7 @@ type var x*: string var y: seq[string] #[ yay inline comments. So nice I have to care bout these. ]# -echo "#", x, "##", y, "#" +echo "#", x, "##", y, "#" & "string" & $test echo (tup, here) echo(argA, argB) diff --git a/nimpretty/tests/expected/exhaustive.nim b/nimpretty/tests/expected/exhaustive.nim index b8c55033ce..8f8a03b0b5 100644 --- a/nimpretty/tests/expected/exhaustive.nim +++ b/nimpretty/tests/expected/exhaustive.nim @@ -3,7 +3,7 @@ discard """ exitcode: "1" """ -import verylongnamehere, verylongnamehere, +import verylongnamehere, verylongnamehere, verylongnamehereverylongnamehereverylong, namehere, verylongnamehere type @@ -17,7 +17,7 @@ type var x*: string var y: seq[string] #[ yay inline comments. So nice I have to care bout these. ]# -echo "#", x, "##", y, "#" +echo "#", x, "##", y, "#" & "string" & $test echo (tup, here) echo(argA, argB) @@ -104,7 +104,7 @@ type 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; +proc openEmitter*[T, S](em: var Emitter; config: ConfigRef; fileIdx: FileIndex) {.pragmaHereWrongCurlyEnd.} = let outfile = changeFileExt(config.toFullPath(fileIdx), ".pretty.nim") em.f = llStreamOpen(outfile, fmWrite) @@ -173,13 +173,13 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) = em.content.len > 0 and em.content[em.content.high] in SymChars+{'_'} proc emitComment(em: var Emitter; tok: TToken) = - let lit = strip fileSection(em.config, em.fid, tok.commentOffsetA, + let lit = strip fileSection(em.config, em.fid, tok.commentOffsetA, tok.commentOffsetB) em.lineSpan = countNewlines(lit) if em.lineSpan > 0: calcCol(em, lit) if not endsInWhite(em): wr(" ") - if em.lineSpan == 0 and max(em.col, + if em.lineSpan == 0 and max(em.col, LineCommentColumn) + lit.len <= MaxLineLen: for i in 1 .. LineCommentColumn - em.col: wr(" ") wr lit @@ -264,7 +264,7 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) = of tkComment: if not preventComment: emitComment(em, tok) - of tkIntLit..tkStrLit, tkRStrLit, tkTripleStrLit, tkGStrLit, + of tkIntLit..tkStrLit, tkRStrLit, tkTripleStrLit, tkGStrLit, tkGTripleStrLit, tkCharLit: let lit = fileSection(em.config, em.fid, tok.offsetA, tok.offsetB) softLinebreak(em, lit) From 26568dff004cf210736cccc8103ce6f2d47cb82f Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 19 Jun 2018 23:48:27 +0200 Subject: [PATCH 19/35] nimpretty: also test on Windows/Appveyor --- appveyor.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/appveyor.yml b/appveyor.yml index a79d32e41c..daa1d4e48f 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -47,6 +47,8 @@ build_script: - koch boot -d:release - koch nimble - nim e tests/test_nimscript.nims + - nim c -o:bin/nimpretty.exe nimpretty/nimpretty.nim + - nim c -r nimpretty/tester.nim - nimble install zip -y - nimble install opengl - nimble install sdl1 From 0725003a8cdb90e149d91fc6c5affcb270223be7 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Wed, 20 Jun 2018 00:56:29 +0200 Subject: [PATCH 20/35] nimpretty: fixes more reported issues --- compiler/layouter.nim | 7 ++++--- nimpretty/tests/exhaustive.nim | 3 +++ nimpretty/tests/expected/exhaustive.nim | 3 +++ 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/compiler/layouter.nim b/compiler/layouter.nim index d0c9a77b93..409b656c96 100644 --- a/compiler/layouter.nim +++ b/compiler/layouter.nim @@ -169,7 +169,7 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) = of tokKeywordLow..tokKeywordHigh: if endsInAlpha(em): wr(" ") - elif not em.inquote and not endsInWhite(em): + elif not em.inquote and not endsInWhite(em) and tok.tokType in oprSet: wr(" ") if not em.inquote: @@ -207,9 +207,9 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) = tkColonColon, tkDot: wr(TokTypeToStr[tok.tokType]) of tkEquals: - if not em.endsInWhite: wr(" ") + if not em.inquote and not em.endsInWhite: wr(" ") wr(TokTypeToStr[tok.tokType]) - wr(" ") + if not em.inquote: wr(" ") of tkOpr, tkDotDot: if tok.strongSpaceA == 0 and tok.strongSpaceB == 0: # if not surrounded by whitespace, don't produce any whitespace either: @@ -224,6 +224,7 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) = wr(" ") rememberSplit(splitBinary) of tkAccent: + if not em.inquote and endsInAlpha(em): wr(" ") wr(TokTypeToStr[tok.tokType]) em.inquote = not em.inquote of tkComment: diff --git a/nimpretty/tests/exhaustive.nim b/nimpretty/tests/exhaustive.nim index 1039e308bd..bca42d4d45 100644 --- a/nimpretty/tests/exhaustive.nim +++ b/nimpretty/tests/exhaustive.nim @@ -5,6 +5,9 @@ discard """ import verylongnamehere,verylongnamehere,verylongnamehereverylongnamehereverylong,namehere,verylongnamehere +proc `[]=`() = discard "index setter" +proc `putter=`() = discard cast[pointer](cast[int](buffer) + size) + type GeneralTokenizer* = object of RootObj ## comment here kind*: TokenClass ## and here diff --git a/nimpretty/tests/expected/exhaustive.nim b/nimpretty/tests/expected/exhaustive.nim index 8f8a03b0b5..4520fb9bdd 100644 --- a/nimpretty/tests/expected/exhaustive.nim +++ b/nimpretty/tests/expected/exhaustive.nim @@ -6,6 +6,9 @@ discard """ import verylongnamehere, verylongnamehere, verylongnamehereverylongnamehereverylong, namehere, verylongnamehere +proc `[]=`() = discard "index setter" +proc `putter=`() = discard cast[pointer](cast[int](buffer) + size) + type GeneralTokenizer* = object of RootObj ## comment here kind*: TokenClass ## and here From 2a662250d4b12a6dfdc488ec369439101fac209c Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Wed, 20 Jun 2018 00:59:01 +0200 Subject: [PATCH 21/35] nimpretty: added more code claimed to not be working --- nimpretty/tests/exhaustive.nim | 13 +++++++++++++ nimpretty/tests/expected/exhaustive.nim | 13 +++++++++++++ 2 files changed, 26 insertions(+) diff --git a/nimpretty/tests/exhaustive.nim b/nimpretty/tests/exhaustive.nim index bca42d4d45..0ce3bde895 100644 --- a/nimpretty/tests/exhaustive.nim +++ b/nimpretty/tests/exhaustive.nim @@ -285,3 +285,16 @@ proc starWasExportMarker*(em: var Emitter) = setLen(em.content, em.content.len-3) em.content.add("*") dec em.col, 2 + +type + Thing = ref object + grade: string + # this name is great + name: string + +proc f() = + var c: char + var str: string + if c == '\\': + # escape char + str &= c diff --git a/nimpretty/tests/expected/exhaustive.nim b/nimpretty/tests/expected/exhaustive.nim index 4520fb9bdd..dd3ff74e89 100644 --- a/nimpretty/tests/expected/exhaustive.nim +++ b/nimpretty/tests/expected/exhaustive.nim @@ -292,3 +292,16 @@ proc starWasExportMarker*(em: var Emitter) = setLen(em.content, em.content.len-3) em.content.add("*") dec em.col, 2 + +type + Thing = ref object + grade: string + # this name is great + name: string + +proc f() = + var c: char + var str: string + if c == '\\': + # escape char + str &= c From 9adfaa7f0704b46f29f3b2f1cdf7d2e83f73ce07 Mon Sep 17 00:00:00 2001 From: Araq Date: Wed, 20 Jun 2018 10:20:42 +0200 Subject: [PATCH 22/35] fixes #8076 --- compiler/ccgstmts.nim | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index 71d212282f..f9654bb1fb 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -978,16 +978,17 @@ proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false): Rope = if isAsmStmt and hasGnuAsm in CC[p.config.cCompiler].props: for x in splitLines(res): var j = 0 - while x[j] in {' ', '\t'}: inc(j) - if x[j] in {'"', ':'}: - # don't modify the line if already in quotes or - # some clobber register list: - add(result, x); add(result, "\L") - elif x[j] != '\0': - # ignore empty lines - add(result, "\"") - add(result, x) - add(result, "\\n\"\n") + while j < x.len and x[j] in {' ', '\t'}: inc(j) + if j < x.len: + if x[j] in {'"', ':'}: + # don't modify the line if already in quotes or + # some clobber register list: + add(result, x); add(result, "\L") + else: + # ignore empty lines + add(result, "\"") + add(result, x) + add(result, "\\n\"\n") else: res.add("\L") result = res.rope From 1be82d96a682338ba53832bcc2772b7865b47b57 Mon Sep 17 00:00:00 2001 From: Araq Date: Wed, 20 Jun 2018 11:35:36 +0200 Subject: [PATCH 23/35] nimpretty: bugfixes; refs #8078 --- compiler/layouter.nim | 11 +++++++---- nimpretty/tester.nim | 2 +- nimpretty/tests/exhaustive.nim | 16 ++++++++++++++++ nimpretty/tests/expected/exhaustive.nim | 18 ++++++++++++++++++ 4 files changed, 42 insertions(+), 5 deletions(-) diff --git a/compiler/layouter.nim b/compiler/layouter.nim index 409b656c96..62844db4bc 100644 --- a/compiler/layouter.nim +++ b/compiler/layouter.nim @@ -74,9 +74,10 @@ template wr(x) = template goodCol(col): bool = col in 40..MaxLineLen const - splitters = {tkComma, tkSemicolon, tkParLe, tkParDotLe, - tkBracketLe, tkBracketLeColon, tkCurlyDotLe, - tkCurlyLe} + openPars = {tkParLe, tkParDotLe, + tkBracketLe, tkBracketLeColon, tkCurlyDotLe, + tkCurlyLe} + splitters = openPars + {tkComma, tkSemicolon} oprSet = {tkOpr, tkDiv, tkMod, tkShl, tkShr, tkIn, tkNotin, tkIs, tkIsnot, tkNot, tkOf, tkAs, tkDotDot, tkAnd, tkOr, tkXor} @@ -169,7 +170,9 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) = of tokKeywordLow..tokKeywordHigh: if endsInAlpha(em): wr(" ") - elif not em.inquote and not endsInWhite(em) and tok.tokType in oprSet: + elif not em.inquote and not endsInWhite(em) and + em.lastTok notin openPars: + #and tok.tokType in oprSet wr(" ") if not em.inquote: diff --git a/nimpretty/tester.nim b/nimpretty/tester.nim index 7db245b5f3..8798ce06ae 100644 --- a/nimpretty/tester.nim +++ b/nimpretty/tester.nim @@ -16,7 +16,7 @@ proc test(infile, outfile: string) = let produced = dir / nimFile & ".pretty" if strip(readFile(expected)) != strip(readFile(produced)): echo "FAILURE: files differ: ", nimFile - discard execShellCmd("diff -uNdr " & produced & " " & expected) + discard execShellCmd("diff -uNdr " & expected & " " & produced) failures += 1 else: echo "SUCCESS: files identical: ", nimFile diff --git a/nimpretty/tests/exhaustive.nim b/nimpretty/tests/exhaustive.nim index 0ce3bde895..9f2141fbba 100644 --- a/nimpretty/tests/exhaustive.nim +++ b/nimpretty/tests/exhaustive.nim @@ -8,6 +8,22 @@ import verylongnamehere,verylongnamehere,verylongnamehereverylongnamehereverylon proc `[]=`() = discard "index setter" proc `putter=`() = discard cast[pointer](cast[int](buffer) + size) +(not false) + +let expr = if true: "true" else: "false" + +var body = newNimNode(nnkIfExpr).add( + newNimNode(nnkElifBranch).add( + infix(newDotExpr(ident("a"), ident("kind")), "==", newDotExpr(ident("b"), ident("kind"))), + condition + ), + newNimNode(nnkElse).add(newStmtList(newNimNode(nnkReturnStmt).add(ident("false")))) +) + +# comment + +var x = 1 + type GeneralTokenizer* = object of RootObj ## comment here kind*: TokenClass ## and here diff --git a/nimpretty/tests/expected/exhaustive.nim b/nimpretty/tests/expected/exhaustive.nim index dd3ff74e89..95071fce33 100644 --- a/nimpretty/tests/expected/exhaustive.nim +++ b/nimpretty/tests/expected/exhaustive.nim @@ -9,6 +9,24 @@ import verylongnamehere, verylongnamehere, proc `[]=`() = discard "index setter" proc `putter=`() = discard cast[pointer](cast[int](buffer) + size) +(not false) + +let expr = if true: "true" else: "false" + +var body = newNimNode(nnkIfExpr).add( + newNimNode(nnkElifBranch).add( + infix(newDotExpr(ident("a"), ident("kind")), "==", newDotExpr(ident("b"), + ident("kind"))), + condition + ), + newNimNode(nnkElse).add(newStmtList(newNimNode(nnkReturnStmt).add(ident( + "false")))) +) + +# comment + +var x = 1 + type GeneralTokenizer* = object of RootObj ## comment here kind*: TokenClass ## and here From cc11aa9698e6537fd412bb64a3f99393bc51c90d Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 22 Jun 2018 03:19:30 +0200 Subject: [PATCH 24/35] varints module: critical bugfix --- lib/std/varints.nim | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/std/varints.nim b/lib/std/varints.nim index bfc1945fef..483d5c96cd 100644 --- a/lib/std/varints.nim +++ b/lib/std/varints.nim @@ -19,7 +19,7 @@ proc readVu64*(z: openArray[byte]; pResult: var uint64): int = return 1 if z[0] <= 248: if z.len < 2: return 0 - pResult = (uint64 z[0] - 241) * 256 + uint64 z[1] + 240 + pResult = (uint64 z[0] - 241) * 256 + z[1].uint64 + 240 return 2 if z.len < int(z[0]-246): return 0 if z[0] == 249: @@ -135,6 +135,13 @@ when isMainModule: if encodeZigzag(decodeZigzag(test)) != test: echo "Failure for ", test, " ", encodeZigzag(decodeZigzag(test)), " ", decodeZigzag(test) + for test in 0u64..300u64: + let wrLen = writeVu64(dest, test) + let rdLen = readVu64(dest, got) + assert wrLen == rdLen + if got != test: + echo "BUG! expected: ", test, " got: ", got, " z0: ", dest[0] + # check this also works for floats: for test in [0.0, 0.1, 2.0, +Inf, Nan, NegInf]: let t = cast[uint64](test) From af66258dca695d932e76ea31bdee2b2f185139cb Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Thu, 21 Jun 2018 22:21:24 +0200 Subject: [PATCH 25/35] Discriminate gensym'd type names in sigHash The root cause of #7905 lies in the codegen phase. The two template instantiations generate two different MyType types with different members but same t.sym.name leading the caching mechanism to confuse the two. Fixes #7905 --- compiler/sighashes.nim | 6 ++++-- tests/types/t7905.nim | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 tests/types/t7905.nim diff --git a/compiler/sighashes.nim b/compiler/sighashes.nim index 0b95387cd9..720d1848db 100644 --- a/compiler/sighashes.nim +++ b/compiler/sighashes.nim @@ -189,10 +189,11 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]) = c.hashTypeSym(t.sym) else: c.hashSym(t.sym) - if sfAnon in t.sym.flags: + if {sfAnon, sfGenSym} * t.sym.flags != {}: # generated object names can be identical, so we need to # disambiguate furthermore by hashing the field types and names: # mild hack to prevent endless recursions (makes nimforum compile again): + let wasAnon = sfAnon in t.sym.flags excl t.sym.flags, sfAnon let n = t.n for i in 0 ..< n.len: @@ -200,7 +201,8 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]) = let s = n[i].sym c.hashSym s c.hashType s.typ, flags - incl t.sym.flags, sfAnon + if wasAnon: + incl t.sym.flags, sfAnon else: c &= t.id if t.len > 0 and t.sons[0] != nil: diff --git a/tests/types/t7905.nim b/tests/types/t7905.nim new file mode 100644 index 0000000000..ddb3710395 --- /dev/null +++ b/tests/types/t7905.nim @@ -0,0 +1,18 @@ +discard """ + output: ''' +(member: "hello world") +(member: 123.456) +''' +""" + +template foobar(arg: typed): untyped = + type + MyType = object + member: type(arg) + + var myVar: MyType + myVar.member = arg + echo myVar + +foobar("hello world") +foobar(123.456'f64) From e39baf46fc523245a7d73b263808b944c6f225db Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Fri, 22 Jun 2018 15:09:35 +0200 Subject: [PATCH 26/35] Don't blow up with recursive objects --- compiler/sighashes.nim | 7 +++---- tests/types/t7905.nim | 15 +++++++++++++++ 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/compiler/sighashes.nim b/compiler/sighashes.nim index 720d1848db..0bf2b84598 100644 --- a/compiler/sighashes.nim +++ b/compiler/sighashes.nim @@ -193,16 +193,15 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]) = # generated object names can be identical, so we need to # disambiguate furthermore by hashing the field types and names: # mild hack to prevent endless recursions (makes nimforum compile again): - let wasAnon = sfAnon in t.sym.flags - excl t.sym.flags, sfAnon + let oldFlags = t.sym.flags + t.sym.flags = t.sym.flags - {sfAnon, sfGenSym} let n = t.n for i in 0 ..< n.len: assert n[i].kind == nkSym let s = n[i].sym c.hashSym s c.hashType s.typ, flags - if wasAnon: - incl t.sym.flags, sfAnon + t.sym.flags = oldFlags else: c &= t.id if t.len > 0 and t.sons[0] != nil: diff --git a/tests/types/t7905.nim b/tests/types/t7905.nim index ddb3710395..ef75bb86cf 100644 --- a/tests/types/t7905.nim +++ b/tests/types/t7905.nim @@ -2,6 +2,8 @@ discard """ output: ''' (member: "hello world") (member: 123.456) +(member: "hello world", x: ...) +(member: 123.456, x: ...) ''' """ @@ -16,3 +18,16 @@ template foobar(arg: typed): untyped = foobar("hello world") foobar(123.456'f64) + +template foobarRec(arg: typed): untyped = + type + MyType = object + member: type(arg) + x: ref MyType + + var myVar: MyType + myVar.member = arg + echo myVar + +foobarRec("hello world") +foobarRec(123.456'f64) From 7f0d7871eda6ec54a941b6a52f00ceb217bfe425 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Sat, 23 Jun 2018 13:37:35 +0200 Subject: [PATCH 27/35] Use the resolved typedesc in semVarOrLet By leaving the unsemanticized node in the AST we'd trip some passes like the Transf one as seen in #7936. --- compiler/semstmts.nim | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 3687e50e9c..3528d0155d 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -521,7 +521,9 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = localError(c.config, a.info, errWrongNumberOfVariables) b = newNodeI(nkVarTuple, a.info) newSons(b, length) - b.sons[length-2] = a.sons[length-2] # keep type desc for doc generator + # keep type desc for doc generator + # NOTE: at the moment this is always ast.emptyNode, see parser.nim + b.sons[length-2] = a.sons[length-2] b.sons[length-1] = def addToVarSection(c, result, n, b) elif tup.kind == tyTuple and def.kind in {nkPar, nkTupleConstr} and @@ -560,7 +562,12 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = # keep documentation information: b.comment = a.comment addSon(b, newSymNode(v)) - addSon(b, a.sons[length-2]) # keep type desc for doc generator + # keep type desc for doc generator, but only if the user explicitly + # added it + if a.sons[length-2].kind != nkEmpty: + addSon(b, newNodeIT(nkType, a.info, typ)) + else: + addSon(b, a.sons[length-2]) addSon(b, copyTree(def)) addToVarSection(c, result, n, b) else: From 371f87e9626ce84a55b3d8e1aa1bc3a97ebcc91b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oscar=20Nihlg=C3=A5rd?= Date: Sat, 23 Jun 2018 15:01:55 +0200 Subject: [PATCH 28/35] Change inconsistent paramaters in times.nim (#8091) --- lib/pure/times.nim | 47 +++++++++++++++++++--------------------------- 1 file changed, 19 insertions(+), 28 deletions(-) diff --git a/lib/pure/times.nim b/lib/pure/times.nim index 7cecc31ab5..7fd60b818f 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -238,11 +238,11 @@ proc utcZoneInfoFromUtc(time: Time): ZonedTime {.tags: [], raises: [], benign .} proc utcZoneInfoFromTz(adjTime: Time): ZonedTime {.tags: [], raises: [], benign .} proc localZoneInfoFromUtc(time: Time): ZonedTime {.tags: [], raises: [], benign .} proc localZoneInfoFromTz(adjTime: Time): ZonedTime {.tags: [], raises: [], benign .} -proc initTime*(unix: int64, nanosecond: NanosecondRange): Time +proc initTime*(unix: int64, nanosecond: NanosecondRange): Time {.tags: [], raises: [], benign noSideEffect.} proc initDuration*(nanoseconds, microseconds, milliseconds, - seconds, minutes, hours, days, weeks: int64 = 0): Duration + seconds, minutes, hours, days, weeks: int64 = 0): Duration {.tags: [], raises: [], benign noSideEffect.} proc nanosecond*(time: Time): NanosecondRange = @@ -531,7 +531,7 @@ proc `$`*(dur: Duration): string = let quantity = numParts[unit] if quantity != 0.int64: parts.add(stringifyUnit(quantity, $unit)) - + result = humanizeParts(parts) proc `+`*(a, b: Duration): Duration {.operator.} = @@ -1243,23 +1243,23 @@ proc evaluateStaticInterval(interval: TimeInterval): Duration = minutes = interval.minutes, hours = interval.hours) -proc between*(startDt, endDt:DateTime): TimeInterval = +proc between*(startDt, endDt: DateTime): TimeInterval = ## Evaluate difference between two dates in ``TimeInterval`` format, so, it ## will be relative. ## - ## **Warning:** It's not recommended to use ``between`` for ``DateTime's`` in - ## different ``TimeZone's``. + ## **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. runnableExamples: - var a = initDateTime(year = 2018, month = Month(3), monthday = 25, + 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, + 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) @@ -1526,7 +1526,6 @@ proc formatToken(dt: DateTime, token: string, buf: var string) = else: raise newException(ValueError, "Invalid format string: " & token) - proc format*(dt: DateTime, f: string): string {.tags: [].}= ## This procedure formats `dt` as specified by `f`. The following format ## specifiers are available: @@ -1601,18 +1600,14 @@ proc format*(dt: DateTime, f: string): string {.tags: [].}= inc(i) formatToken(dt, currentF, result) -proc format*(time: Time, f: string, zone_info: proc(t: Time): DateTime): string {.tags: [].} = - ## converts a `Time` value to a string representation. It will use format from +proc format*(time: Time, f: string, zone: Timezone = local()): string {.tags: [].} = + ## Converts a `Time` value to a string representation. It will use format from ## ``format(dt: DateTime, f: string)``. runnableExamples: - var dt = initDateTime(01, mJan, 1970, 00, 00, 00, local()) + var dt = initDateTime(01, mJan, 1970, 00, 00, 00, utc()) var tm = dt.toTime() - doAssert format(tm, "yyyy-MM-dd'T'HH:mm:ss", local) == "1970-01-01T00:00:00" - dt = initDateTime(01, mJan, 1970, 00, 00, 00, utc()) - tm = dt.toTime() - doAssert format(tm, "yyyy-MM-dd'T'HH:mm:ss", utc) == "1970-01-01T00:00:00" - - zone_info(time).format(f) + doAssert format(tm, "yyyy-MM-dd'T'HH:mm:ss", utc()) == "1970-01-01T00:00:00" + time.inZone(zone).format(f) proc `$`*(dt: DateTime): string {.tags: [], raises: [], benign.} = ## Converts a `DateTime` object to a string representation. @@ -1984,16 +1979,12 @@ proc countYearsAndDays*(daySpan: int): tuple[years: int, days: int] = proc toTimeInterval*(time: Time): TimeInterval = ## Converts a Time to a TimeInterval. ## - ## To be used when diffing times. + ## To be used when diffing times. Consider using `between` instead. runnableExamples: let a = fromUnix(10) - let dt = initDateTime(01, mJan, 1970, 00, 00, 00, local()) - doAssert a.toTimeInterval() == initTimeInterval( - years=1970, days=1, seconds=10, hours=convert( - Seconds, Hours, -dt.utcOffset - ) - ) - + let b = fromUnix(1_500_000_000) + let ti = b.toTimeInterval() - a.toTimeInterval() + doAssert a + ti == b var dt = time.local initTimeInterval(dt.nanosecond, 0, 0, dt.second, dt.minute, dt.hour, dt.monthday, 0, dt.month.ord - 1, dt.year) @@ -2150,7 +2141,7 @@ proc timeToTimeInterval*(t: Time): TimeInterval {.deprecated.} = t.toTimeInterval() proc getDayOfWeek*(day, month, year: int): WeekDay {.tags: [], raises: [], benign, deprecated.} = - ## **Deprecated since v0.18.0:** use + ## **Deprecated since v0.18.0:** use ## ``getDayOfWeek(monthday: MonthdayRange; month: Month; year: int)`` instead. getDayOfWeek(day, month.Month, year) From 059ddeee10a7b6e3805891b68205e01c045e038d Mon Sep 17 00:00:00 2001 From: Federico Ceratto Date: Sat, 23 Jun 2018 14:32:24 +0100 Subject: [PATCH 29/35] Minor doc fix --- lib/pure/httpclient.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim index 8530e4c42f..8b4fb0f8c1 100644 --- a/lib/pure/httpclient.nim +++ b/lib/pure/httpclient.nim @@ -1184,7 +1184,7 @@ proc request*(client: HttpClient | AsyncHttpClient, url: string, ## Connects to the hostname specified by the URL and performs a request ## using the custom method string specified by ``httpMethod``. ## - ## Connection will kept alive. Further requests on the same ``client`` to + ## Connection will be kept alive. Further requests on the same ``client`` to ## the same hostname will not require a new connection to be made. The ## connection can be closed by using the ``close`` procedure. ## From 4c2e712056b1e23d89be51f9cdcbfe573c399819 Mon Sep 17 00:00:00 2001 From: Kaushal Modi Date: Sun, 24 Jun 2018 03:53:20 -0400 Subject: [PATCH 30/35] Support setting foreground/background colors to terminal defaults (#8073) * Support setting foreground/background colors to terminal defaults Adds fgDefault to ForegroundColor and bgDefault to BackgroundColor enums. For Windows console, the default foreground color is assumed to be white, and the default background color black. * Add default fg/bg color caching for Windows Console --- lib/pure/terminal.nim | 40 ++++++++++++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/lib/pure/terminal.nim b/lib/pure/terminal.nim index 7ad243150a..ac41a0aad3 100644 --- a/lib/pure/terminal.nim +++ b/lib/pure/terminal.nim @@ -542,7 +542,9 @@ type fgBlue, ## blue fgMagenta, ## magenta fgCyan, ## cyan - fgWhite ## white + fgWhite, ## white + fg8Bit, ## 256-color (not supported, see ``enableTrueColors`` instead.) + fgDefault ## default terminal foreground color BackgroundColor* = enum ## terminal's background colors bgBlack = 40, ## black @@ -552,28 +554,40 @@ type bgBlue, ## blue bgMagenta, ## magenta bgCyan, ## cyan - bgWhite ## white + bgWhite, ## white + 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 + proc setForegroundColor*(f: File, fg: ForegroundColor, bright=false) = ## Sets the terminal's foreground color. when defined(windows): let h = conHandle(f) var old = getAttributes(h) and not FOREGROUND_RGB + if defaultForegroundColor == 0xFFFF'i16: + defaultForegroundColor = old old = if bright: old or FOREGROUND_INTENSITY else: old and not(FOREGROUND_INTENSITY) const lookup: array[ForegroundColor, int] = [ - 0, + 0, # ForegroundColor enum with ordinal 30 (FOREGROUND_RED), (FOREGROUND_GREEN), (FOREGROUND_RED or FOREGROUND_GREEN), (FOREGROUND_BLUE), (FOREGROUND_RED or FOREGROUND_BLUE), (FOREGROUND_BLUE or FOREGROUND_GREEN), - (FOREGROUND_BLUE or FOREGROUND_GREEN or FOREGROUND_RED)] - discard setConsoleTextAttribute(h, toU16(old or lookup[fg])) + (FOREGROUND_BLUE or FOREGROUND_GREEN or FOREGROUND_RED), + 0, # fg8Bit not supported, see ``enableTrueColors`` instead. + 0] # unused + if fg == fgDefault: + discard setConsoleTextAttribute(h, toU16(old or defaultForegroundColor)) + else: + discard setConsoleTextAttribute(h, toU16(old or lookup[fg])) else: gFG = ord(fg) if bright: inc(gFG, 60) @@ -584,18 +598,25 @@ proc setBackgroundColor*(f: File, bg: BackgroundColor, bright=false) = when defined(windows): let h = conHandle(f) var old = getAttributes(h) and not BACKGROUND_RGB + if defaultBackgroundColor == 0xFFFF'i16: + defaultBackgroundColor = old old = if bright: old or BACKGROUND_INTENSITY else: old and not(BACKGROUND_INTENSITY) const lookup: array[BackgroundColor, int] = [ - 0, + 0, # BackgroundColor enum with ordinal 40 (BACKGROUND_RED), (BACKGROUND_GREEN), (BACKGROUND_RED or BACKGROUND_GREEN), (BACKGROUND_BLUE), (BACKGROUND_RED or BACKGROUND_BLUE), (BACKGROUND_BLUE or BACKGROUND_GREEN), - (BACKGROUND_BLUE or BACKGROUND_GREEN or BACKGROUND_RED)] - discard setConsoleTextAttribute(h, toU16(old or lookup[bg])) + (BACKGROUND_BLUE or BACKGROUND_GREEN or BACKGROUND_RED), + 0, # bg8Bit not supported, see ``enableTrueColors`` instead. + 0] # unused + if bg == bgDefault: + discard setConsoleTextAttribute(h, toU16(old or defaultBackgroundColor)) + else: + discard setConsoleTextAttribute(h, toU16(old or lookup[bg])) else: gBG = ord(bg) if bright: inc(gBG, 60) @@ -935,3 +956,6 @@ when not defined(testing) and isMainModule: stdout.styledWrite(fgGreen, "green text") echo "" echo "ordinary text" + stdout.styledWriteLine(fgRed, "red text ", styleBright, "bold red", fgDefault, " bold text") + stdout.styledWriteLine(bgYellow, "text in yellow bg", styleBright, " bold text in yellow bg", bgDefault, " bold text") + echo "ordinary text" From bfa3d62cc1a9485cc7030216f5859258131ea5bb Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Sun, 24 Jun 2018 15:13:34 +0200 Subject: [PATCH 31/35] More concept fixes Fixes #7705, #7703, #7702 --- compiler/semexprs.nim | 4 ++-- compiler/semfold.nim | 2 +- compiler/types.nim | 2 +- tests/concepts/treversable.nim | 31 +++++++++++++++++++++++++++++++ 4 files changed, 35 insertions(+), 4 deletions(-) create mode 100644 tests/concepts/treversable.nim diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 9d7c493a7b..d7b5667b9a 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -271,7 +271,7 @@ proc semLowHigh(c: PContext, n: PNode, m: TMagic): PNode = localError(c.config, n.info, errXExpectsTypeOrValue % opToStr[m]) else: n.sons[1] = semExprWithType(c, n.sons[1], {efDetermineType}) - var typ = skipTypes(n.sons[1].typ, abstractVarRange + {tyTypeDesc}) + var typ = skipTypes(n.sons[1].typ, abstractVarRange + {tyTypeDesc, tyUserTypeClassInst}) case typ.kind of tySequence, tyString, tyCString, tyOpenArray, tyVarargs: n.typ = getSysType(c.graph, n.info, tyInt) @@ -1261,7 +1261,7 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode = # make sure we don't evaluate generic macros/templates n.sons[0] = semExprWithType(c, n.sons[0], {efNoEvaluateGeneric}) - let arr = skipTypes(n.sons[0].typ, {tyGenericInst, + let arr = skipTypes(n.sons[0].typ, {tyGenericInst, tyUserTypeClassInst, tyVar, tyLent, tyPtr, tyRef, tyAlias, tySink}) case arr.kind of tyArray, tyOpenArray, tyVarargs, tySequence, tyString, diff --git a/compiler/semfold.nim b/compiler/semfold.nim index eceb104700..2f495bc7f1 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -619,7 +619,7 @@ proc getConstExpr(m: PSym, n: PNode; g: ModuleGraph): PNode = of mLow: result = newIntNodeT(firstOrd(g.config, n.sons[1].typ), n, g) of mHigh: - if skipTypes(n.sons[1].typ, abstractVar).kind notin + if skipTypes(n.sons[1].typ, abstractVar+{tyUserTypeClassInst}).kind notin {tySequence, tyString, tyCString, tyOpenArray, tyVarargs}: result = newIntNodeT(lastOrd(g.config, skipTypes(n[1].typ, abstractVar)), n, g) else: diff --git a/compiler/types.nim b/compiler/types.nim index 98343c6889..a1bdc77305 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -615,7 +615,7 @@ proc firstOrd*(conf: ConfigRef; t: PType): BiggestInt = else: assert(t.n.sons[0].kind == nkSym) result = t.n.sons[0].sym.position - of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tyStatic, tyInferred: + of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tyStatic, tyInferred, tyUserTypeClassInst: result = firstOrd(conf, lastSon(t)) of tyOrdinal: if t.len > 0: result = firstOrd(conf, lastSon(t)) diff --git a/tests/concepts/treversable.nim b/tests/concepts/treversable.nim new file mode 100644 index 0000000000..6ebc077d90 --- /dev/null +++ b/tests/concepts/treversable.nim @@ -0,0 +1,31 @@ +# issue 7705, 7703, 7702 +discard """ + output: ''' +z +e + ''' +""" + +type + Reversable*[T] = concept a + a[int] is T + a.high is int + a.len is int + a.low is int + +proc get[T](s: Reversable[T], n: int): T = + s[n] + +proc hi[T](s: Reversable[T]): int = + s.high + +proc lo[T](s: Reversable[T]): int = + s.low + +iterator reverse*[T](s: Reversable[T]): T = + assert hi(s) - lo(s) == len(s) - 1 + for z in hi(s).countdown(lo(s)): + yield s.get(z) + +for s in @["e", "z"].reverse: + echo s From 589d6bc8d81408df88298dd6cd3ee176d4bdbc46 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Mon, 25 Jun 2018 15:46:38 +0200 Subject: [PATCH 32/35] Warn the user if the specified verbosity level is wrong --- compiler/commands.nim | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/compiler/commands.nim b/compiler/commands.nim index 330504a76e..866405f9f5 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -606,7 +606,10 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; incl(conf.globalOptions, optRun) of "verbosity": expectArg(conf, switch, arg, pass, info) - conf.verbosity = parseInt(arg) + let verbosity = parseInt(arg) + if verbosity notin {0..3}: + localError(conf, info, "invalid verbosity level: '$1'" % arg) + conf.verbosity = verbosity conf.notes = NotesVerbosity[conf.verbosity] incl(conf.notes, conf.enableNotes) excl(conf.notes, conf.disableNotes) From aef441101fcfd8390f9a28777ae2bef8b1c5b5b2 Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 26 Jun 2018 01:34:59 +0200 Subject: [PATCH 33/35] fixes docgen regression caused by refactorings; fixes #8097 --- compiler/docgen.nim | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index d463dc3c0f..db4e301d4b 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -582,7 +582,7 @@ proc traceDeps(d: PDoc, it: PNode) = if d.section[k] != nil: add(d.section[k], ", ") dispA(d.conf, d.section[k], "$1", - "$1", [rope(getModuleName(d.conf, it))]) + "$1", [rope(splitFile(getModuleName(d.conf, it)).name)]) proc generateDoc*(d: PDoc, n: PNode) = case n.kind @@ -780,13 +780,11 @@ proc getOutFile2(conf: ConfigRef; filename, ext, dir: string): string = proc writeOutput*(d: PDoc, filename, outExt: string, useWarning = false) = var content = genOutFile(d) var success = true - var filename: string if optStdout in d.conf.globalOptions: writeRope(stdout, content) - filename = "" else: - filename = getOutFile2(d.conf, filename, outExt, "htmldocs") - success = writeRope(content, filename) + let outfile = getOutFile2(d.conf, filename, outExt, "htmldocs") + success = writeRope(content, outfile) if not success: rawMessage(d.conf, if useWarning: warnCannotOpenFile else: errCannotOpenFile, filename) From 5976bd96be3d14e033005333d8ddab85b2ee7874 Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 26 Jun 2018 01:43:41 +0200 Subject: [PATCH 34/35] nimpretty: detect '; vs ,' style based on the first usage of the token in parameter lists --- compiler/layouter.nim | 8 +++++++- nimpretty/tests/exhaustive.nim | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/compiler/layouter.nim b/compiler/layouter.nim index 62844db4bc..36ad086968 100644 --- a/compiler/layouter.nim +++ b/compiler/layouter.nim @@ -20,11 +20,15 @@ type SplitKind = enum splitComma, splitParLe, splitAnd, splitOr, splitIn, splitBinary + SemicolonKind = enum + detectSemicolonKind, useSemicolon, dontTouch + Emitter* = object config: ConfigRef fid: FileIndex lastTok: TTokType inquote: bool + semicolons: SemicolonKind col, lastLineNumber, lineSpan, indentLevel, indWidth: int nested: int doIndentMore*: int @@ -258,7 +262,9 @@ proc starWasExportMarker*(em: var Emitter) = dec em.col, 2 proc commaWasSemicolon*(em: var Emitter) = - if em.content.endsWith(", "): + 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("; ") diff --git a/nimpretty/tests/exhaustive.nim b/nimpretty/tests/exhaustive.nim index 9f2141fbba..a2501a1937 100644 --- a/nimpretty/tests/exhaustive.nim +++ b/nimpretty/tests/exhaustive.nim @@ -122,7 +122,7 @@ type 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, fileIdx: FileIndex) {.pragmaHereWrongCurlyEnd} = +proc openEmitter*[T, S](em: var Emitter; config: ConfigRef, fileIdx: FileIndex) {.pragmaHereWrongCurlyEnd} = let outfile = changeFileExt(config.toFullPath(fileIdx), ".pretty.nim") em.f = llStreamOpen(outfile, fmWrite) em.config = config From 7e89f9a09a2b181fae6e6e3caaf7bea781c3e864 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Wollenschl=C3=A4ger?= Date: Tue, 26 Jun 2018 20:08:40 +0900 Subject: [PATCH 35/35] Don't remove extension of source files twice, if filenames contain dots --- compiler/extccomp.nim | 2 +- compiler/pragmas.nim | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index 615b8c1e1c..17133624b4 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -603,7 +603,7 @@ proc addExternalFileToCompile*(conf: ConfigRef; c: var Cfile) = proc addExternalFileToCompile*(conf: ConfigRef; filename: string) = var c = Cfile(cname: filename, - obj: toObjFile(conf, completeCFilePath(conf, changeFileExt(filename, ""), false)), + obj: toObjFile(conf, completeCFilePath(conf, filename, false)), flags: {CfileFlag.External}) addExternalFileToCompile(conf, c) diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index c78a3519cf..afe60e9dd9 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -466,7 +466,7 @@ proc processCompile(c: PContext, n: PNode) = else: found = findFile(c.config, s) if found.len == 0: found = s - let obj = toObjFile(c.config, completeCFilePath(c.config, changeFileExt(found, ""), false)) + let obj = toObjFile(c.config, completeCFilePath(c.config, found, false)) docompile(c, it, found, obj) proc processCommonLink(c: PContext, n: PNode, feature: TLinkFeature) =