exhaustive.nim 8.2 KB

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