exhaustive.nim 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. discard """
  2. outputsub: '''ObjectAssignmentError'''
  3. exitcode: "1"
  4. """
  5. import verylongnamehere,verylongnamehere,verylongnamehereverylongnamehereverylong,namehere,verylongnamehere
  6. proc `[]=`() = discard "index setter"
  7. proc `putter=`() = discard cast[pointer](cast[int](buffer) + size)
  8. (not false)
  9. let expr = if true: "true" else: "false"
  10. var body = newNimNode(nnkIfExpr).add(
  11. newNimNode(nnkElifBranch).add(
  12. infix(newDotExpr(ident("a"), ident("kind")), "==", newDotExpr(ident("b"), ident("kind"))),
  13. condition
  14. ),
  15. newNimNode(nnkElse).add(newStmtList(newNimNode(nnkReturnStmt).add(ident("false"))))
  16. )
  17. # comment
  18. var x = 1
  19. type
  20. GeneralTokenizer* = object of RootObj ## comment here
  21. kind*: TokenClass ## and here
  22. start*, length*: int ## you know how it goes...
  23. buf: cstring
  24. pos: int # other comment here.
  25. state: TokenClass
  26. var x*: string
  27. var y: seq[string] #[ yay inline comments. So nice I have to care bout these. ]#
  28. echo "#", x, "##", y, "#" & "string" & $test
  29. echo (tup, here)
  30. echo(argA, argB)
  31. import macros
  32. ## A documentation comment here.
  33. ## That spans multiple lines.
  34. ## And is not to be touched.
  35. const numbers = [4u8, 5'u16, 89898_00]
  36. macro m(n): untyped =
  37. result = foo"string literal"
  38. {.push m.}
  39. proc p() = echo "p", 1+4 * 5, if true: 5 else: 6
  40. proc q(param: var ref ptr string) =
  41. p()
  42. if true:
  43. echo a and b or not c and not -d
  44. {.pop.}
  45. q()
  46. when false:
  47. # bug #4766
  48. type
  49. Plain = ref object
  50. discard
  51. Wrapped[T] = object
  52. value: T
  53. converter toWrapped[T](value: T): Wrapped[T] =
  54. Wrapped[T](value: value)
  55. let result = Plain()
  56. discard $result
  57. when false:
  58. # bug #3670
  59. template someTempl(someConst: bool) =
  60. when someConst:
  61. var a: int
  62. if true:
  63. when not someConst:
  64. var a: int
  65. a = 5
  66. someTempl(true)
  67. #
  68. #
  69. # The Nim Compiler
  70. # (c) Copyright 2018 Andreas Rumpf
  71. #
  72. # See the file "copying.txt", included in this
  73. # distribution, for details about the copyright.
  74. #
  75. ## Layouter for nimpretty. Still primitive but useful.
  76. import idents, lexer, lineinfos, llstream, options, msgs, strutils
  77. from os import changeFileExt
  78. const
  79. MaxLineLen = 80
  80. LineCommentColumn = 30
  81. type
  82. SplitKind = enum
  83. splitComma, splitParLe, splitAnd, splitOr, splitIn, splitBinary
  84. Emitter* = object
  85. f: PLLStream
  86. config: ConfigRef
  87. fid: FileIndex
  88. lastTok: TTokType
  89. inquote {.pragmaHereWrongCurlyEnd}: bool
  90. col, lastLineNumber, lineSpan, indentLevel: int
  91. content: string
  92. fixedUntil: int # marks where we must not go in the content
  93. altSplitPos: array[SplitKind, int] # alternative split positions
  94. proc openEmitter*[T, S](em: var Emitter; config: ConfigRef, fileIdx: FileIndex) {.pragmaHereWrongCurlyEnd} =
  95. let outfile = changeFileExt(config.toFullPath(fileIdx), ".pretty.nim")
  96. em.f = llStreamOpen(outfile, fmWrite)
  97. em.config = config
  98. em.fid = fileIdx
  99. em.lastTok = tkInvalid
  100. em.inquote = false
  101. em.col = 0
  102. em.content = newStringOfCap(16_000)
  103. if em.f == nil:
  104. rawMessage(config, errGenerated, "cannot open file: " & outfile)
  105. proc closeEmitter*(em: var Emitter) {.inline.} =
  106. em.f.llStreamWrite em.content
  107. llStreamClose(em.f)
  108. proc countNewlines(s: string): int =
  109. result = 0
  110. for i in 0..<s.len:
  111. if s[i+1] == '\L': inc result
  112. proc calcCol(em: var Emitter; s: string) =
  113. var i = s.len-1
  114. em.col = 0
  115. while i >= 0 and s[i] != '\L':
  116. dec i
  117. inc em.col
  118. template wr(x) =
  119. em.content.add x
  120. inc em.col, x.len
  121. template goodCol(col): bool = col in 40..MaxLineLen
  122. const splitters = {tkComma, tkSemicolon, tkParLe, tkParDotLe,
  123. tkBracketLe, tkBracketLeColon, tkCurlyDotLe,
  124. tkCurlyLe}
  125. template rememberSplit(kind) =
  126. if goodCol(em.col):
  127. em.altSplitPos[kind] = em.content.len
  128. proc softLinebreak(em: var Emitter, lit: string) =
  129. # XXX Use an algorithm that is outlined here:
  130. # https://llvm.org/devmtg/2013-04/jasper-slides.pdf
  131. # +2 because we blindly assume a comma or ' &' might follow
  132. if not em.inquote and em.col+lit.len+2 >= MaxLineLen:
  133. if em.lastTok in splitters:
  134. wr("\L")
  135. em.col = 0
  136. for i in 1..em.indentLevel+2: wr(" ")
  137. else:
  138. # search backwards for a good split position:
  139. for a in em.altSplitPos:
  140. if a > em.fixedUntil:
  141. let ws = "\L" & repeat(' ',em.indentLevel+2)
  142. em.col = em.content.len - a
  143. em.content.insert(ws, a)
  144. break
  145. proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) =
  146. template endsInWhite(em): bool =
  147. em.content.len > 0 and em.content[em.content.high] in {' ', '\L'}
  148. template endsInAlpha(em): bool =
  149. em.content.len > 0 and em.content[em.content.high] in SymChars+{'_'}
  150. proc emitComment(em: var Emitter; tok: TToken) =
  151. let lit = strip fileSection(em.config, em.fid, tok.commentOffsetA, tok.commentOffsetB)
  152. em.lineSpan = countNewlines(lit)
  153. if em.lineSpan > 0: calcCol(em, lit)
  154. if not endsInWhite(em):
  155. wr(" ")
  156. if em.lineSpan == 0 and max(em.col, LineCommentColumn) + lit.len <= MaxLineLen:
  157. for i in 1 .. LineCommentColumn - em.col: wr(" ")
  158. wr lit
  159. var preventComment = case tok.tokType
  160. of tokKeywordLow..tokKeywordHigh:
  161. if endsInAlpha(em): wr(" ")
  162. wr(TokTypeToStr[tok.tokType])
  163. case tok.tokType
  164. of tkAnd: rememberSplit(splitAnd)
  165. of tkOr: rememberSplit(splitOr)
  166. of tkIn: rememberSplit(splitIn)
  167. else: 90
  168. else:
  169. "case returns value"
  170. if tok.tokType == tkComment and tok.line == em.lastLineNumber and tok.indent >= 0:
  171. # we have an inline comment so handle it before the indentation token:
  172. emitComment(em, tok)
  173. preventComment = true
  174. em.fixedUntil = em.content.high
  175. elif tok.indent >= 0:
  176. em.indentLevel = tok.indent
  177. # remove trailing whitespace:
  178. while em.content.len > 0 and em.content[em.content.high] == ' ':
  179. setLen(em.content, em.content.len-1)
  180. wr("\L")
  181. for i in 2..tok.line - em.lastLineNumber: wr("\L")
  182. em.col = 0
  183. for i in 1..tok.indent:
  184. wr(" ")
  185. em.fixedUntil = em.content.high
  186. case tok.tokType
  187. of tokKeywordLow..tokKeywordHigh:
  188. if endsInAlpha(em): wr(" ")
  189. wr(TokTypeToStr[tok.tokType])
  190. case tok.tokType
  191. of tkAnd: rememberSplit(splitAnd)
  192. of tkOr: rememberSplit(splitOr)
  193. of tkIn: rememberSplit(splitIn)
  194. else: discard
  195. of tkColon:
  196. wr(TokTypeToStr[tok.tokType])
  197. wr(" ")
  198. of tkSemicolon,
  199. tkComma:
  200. wr(TokTypeToStr[tok.tokType])
  201. wr(" ")
  202. rememberSplit(splitComma)
  203. of tkParLe, tkParRi, tkBracketLe,
  204. tkBracketRi, tkCurlyLe, tkCurlyRi,
  205. tkBracketDotLe, tkBracketDotRi,
  206. tkCurlyDotLe, tkCurlyDotRi,
  207. tkParDotLe, tkParDotRi,
  208. tkColonColon, tkDot, tkBracketLeColon:
  209. wr(TokTypeToStr[tok.tokType])
  210. if tok.tokType in splitters:
  211. rememberSplit(splitParLe)
  212. of tkEquals:
  213. if not em.endsInWhite: wr(" ")
  214. wr(TokTypeToStr[tok.tokType])
  215. wr(" ")
  216. of tkOpr, tkDotDot:
  217. if not em.endsInWhite: wr(" ")
  218. wr(tok.ident.s)
  219. template isUnary(tok): bool =
  220. tok.strongSpaceB == 0 and tok.strongSpaceA > 0
  221. if not isUnary(tok) or em.lastTok in {tkOpr, tkDotDot}:
  222. wr(" ")
  223. rememberSplit(splitBinary)
  224. of tkAccent:
  225. wr(TokTypeToStr[tok.tokType])
  226. em.inquote = not em.inquote
  227. of tkComment:
  228. if not preventComment:
  229. emitComment(em, tok)
  230. of tkIntLit..tkStrLit, tkRStrLit, tkTripleStrLit, tkGStrLit, tkGTripleStrLit, tkCharLit:
  231. let lit = fileSection(em.config, em.fid, tok.offsetA, tok.offsetB)
  232. softLinebreak(em, lit)
  233. if endsInAlpha(em) and tok.tokType notin {tkGStrLit, tkGTripleStrLit}: wr(" ")
  234. em.lineSpan = countNewlines(lit)
  235. if em.lineSpan > 0: calcCol(em, lit)
  236. wr lit
  237. of tkEof: discard
  238. else:
  239. let lit = if tok.ident != nil: tok.ident.s else: tok.literal
  240. softLinebreak(em, lit)
  241. if endsInAlpha(em): wr(" ")
  242. wr lit
  243. em.lastTok = tok.tokType
  244. em.lastLineNumber = tok.line + em.lineSpan
  245. em.lineSpan = 0
  246. proc starWasExportMarker*(em: var Emitter) =
  247. if em.content.endsWith(" * "):
  248. setLen(em.content, em.content.len-3)
  249. em.content.add("*")
  250. dec em.col, 2
  251. type
  252. Thing = ref object
  253. grade: string
  254. # this name is great
  255. name: string
  256. proc f() =
  257. var c: char
  258. var str: string
  259. if c == '\\':
  260. # escape char
  261. str &= c