llstream.nim 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. #
  2. #
  3. # The Nim Compiler
  4. # (c) Copyright 2012 Andreas Rumpf
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. ## Low-level streams for high performance.
  10. import
  11. pathutils
  12. # support `useGnuReadline`, `useLinenoise` for backwards compatibility
  13. const hasRstdin = (defined(nimUseLinenoise) or defined(useLinenoise) or defined(useGnuReadline)) and
  14. not defined(windows)
  15. when hasRstdin: import rdstdin
  16. type
  17. TLLRepl* = proc (s: PLLStream, buf: pointer, bufLen: int): int
  18. TLLStreamKind* = enum # enum of different stream implementations
  19. llsNone, # null stream: reading and writing has no effect
  20. llsString, # stream encapsulates a string
  21. llsFile, # stream encapsulates a file
  22. llsStdIn # stream encapsulates stdin
  23. TLLStream* = object of RootObj
  24. kind*: TLLStreamKind # accessible for low-level access (lexbase uses this)
  25. f*: File
  26. s*: string
  27. rd*, wr*: int # for string streams
  28. lineOffset*: int # for fake stdin line numbers
  29. repl*: TLLRepl # gives stdin control to clients
  30. PLLStream* = ref TLLStream
  31. proc llStreamOpen*(data: string): PLLStream =
  32. new(result)
  33. result.s = data
  34. result.kind = llsString
  35. proc llStreamOpen*(f: File): PLLStream =
  36. new(result)
  37. result.f = f
  38. result.kind = llsFile
  39. proc llStreamOpen*(filename: AbsoluteFile, mode: FileMode): PLLStream =
  40. new(result)
  41. result.kind = llsFile
  42. if not open(result.f, filename.string, mode): result = nil
  43. proc llStreamOpen*(): PLLStream =
  44. new(result)
  45. result.kind = llsNone
  46. proc llReadFromStdin(s: PLLStream, buf: pointer, bufLen: int): int
  47. proc llStreamOpenStdIn*(r: TLLRepl = llReadFromStdin): PLLStream =
  48. new(result)
  49. result.kind = llsStdIn
  50. result.s = ""
  51. result.lineOffset = -1
  52. result.repl = r
  53. proc llStreamClose*(s: PLLStream) =
  54. case s.kind
  55. of llsNone, llsString, llsStdIn:
  56. discard
  57. of llsFile:
  58. close(s.f)
  59. when not declared(readLineFromStdin):
  60. # fallback implementation:
  61. proc readLineFromStdin(prompt: string, line: var string): bool =
  62. stderr.write(prompt)
  63. result = readLine(stdin, line)
  64. if not result:
  65. stderr.write("\n")
  66. quit(0)
  67. proc endsWith*(x: string, s: set[char]): bool =
  68. var i = x.len-1
  69. while i >= 0 and x[i] == ' ': dec(i)
  70. if i >= 0 and x[i] in s:
  71. result = true
  72. const
  73. LineContinuationOprs = {'+', '-', '*', '/', '\\', '<', '>', '!', '?', '^',
  74. '|', '%', '&', '$', '@', '~', ','}
  75. AdditionalLineContinuationOprs = {'#', ':', '='}
  76. proc endsWithOpr*(x: string): bool =
  77. result = x.endsWith(LineContinuationOprs)
  78. proc continueLine(line: string, inTripleString: bool): bool {.inline.} =
  79. result = inTripleString or line.len > 0 and (
  80. line[0] == ' ' or
  81. line.endsWith(LineContinuationOprs+AdditionalLineContinuationOprs))
  82. proc countTriples(s: string): int =
  83. var i = 0
  84. while i+2 < s.len:
  85. if s[i] == '"' and s[i+1] == '"' and s[i+2] == '"':
  86. inc result
  87. inc i, 2
  88. inc i
  89. proc llReadFromStdin(s: PLLStream, buf: pointer, bufLen: int): int =
  90. s.s = ""
  91. s.rd = 0
  92. var line = newStringOfCap(120)
  93. var triples = 0
  94. while readLineFromStdin(if s.s.len == 0: ">>> " else: "... ", line):
  95. s.s.add(line)
  96. s.s.add("\n")
  97. inc triples, countTriples(line)
  98. if not continueLine(line, (triples and 1) == 1): break
  99. inc(s.lineOffset)
  100. result = min(bufLen, s.s.len - s.rd)
  101. if result > 0:
  102. copyMem(buf, addr(s.s[s.rd]), result)
  103. inc(s.rd, result)
  104. proc llStreamRead*(s: PLLStream, buf: pointer, bufLen: int): int =
  105. case s.kind
  106. of llsNone:
  107. result = 0
  108. of llsString:
  109. result = min(bufLen, s.s.len - s.rd)
  110. if result > 0:
  111. copyMem(buf, addr(s.s[0 + s.rd]), result)
  112. inc(s.rd, result)
  113. of llsFile:
  114. result = readBuffer(s.f, buf, bufLen)
  115. of llsStdIn:
  116. result = s.repl(s, buf, bufLen)
  117. proc llStreamReadLine*(s: PLLStream, line: var string): bool =
  118. setLen(line, 0)
  119. case s.kind
  120. of llsNone:
  121. result = true
  122. of llsString:
  123. while s.rd < s.s.len:
  124. case s.s[s.rd]
  125. of '\x0D':
  126. inc(s.rd)
  127. if s.s[s.rd] == '\x0A': inc(s.rd)
  128. break
  129. of '\x0A':
  130. inc(s.rd)
  131. break
  132. else:
  133. line.add(s.s[s.rd])
  134. inc(s.rd)
  135. result = line.len > 0 or s.rd < s.s.len
  136. of llsFile:
  137. result = readLine(s.f, line)
  138. of llsStdIn:
  139. result = readLine(stdin, line)
  140. proc llStreamWrite*(s: PLLStream, data: string) =
  141. case s.kind
  142. of llsNone, llsStdIn:
  143. discard
  144. of llsString:
  145. s.s.add(data)
  146. inc(s.wr, data.len)
  147. of llsFile:
  148. write(s.f, data)
  149. proc llStreamWriteln*(s: PLLStream, data: string) =
  150. llStreamWrite(s, data)
  151. llStreamWrite(s, "\n")
  152. proc llStreamWrite*(s: PLLStream, data: char) =
  153. var c: char
  154. case s.kind
  155. of llsNone, llsStdIn:
  156. discard
  157. of llsString:
  158. s.s.add(data)
  159. inc(s.wr)
  160. of llsFile:
  161. c = data
  162. discard writeBuffer(s.f, addr(c), sizeof(c))
  163. proc llStreamWrite*(s: PLLStream, buf: pointer, buflen: int) =
  164. case s.kind
  165. of llsNone, llsStdIn:
  166. discard
  167. of llsString:
  168. if buflen > 0:
  169. setLen(s.s, s.s.len + buflen)
  170. copyMem(addr(s.s[0 + s.wr]), buf, buflen)
  171. inc(s.wr, buflen)
  172. of llsFile:
  173. discard writeBuffer(s.f, buf, buflen)
  174. proc llStreamReadAll*(s: PLLStream): string =
  175. const
  176. bufSize = 2048
  177. case s.kind
  178. of llsNone, llsStdIn:
  179. result = ""
  180. of llsString:
  181. if s.rd == 0: result = s.s
  182. else: result = substr(s.s, s.rd)
  183. s.rd = s.s.len
  184. of llsFile:
  185. result = newString(bufSize)
  186. var bytes = readBuffer(s.f, addr(result[0]), bufSize)
  187. var i = bytes
  188. while bytes == bufSize:
  189. setLen(result, i + bufSize)
  190. bytes = readBuffer(s.f, addr(result[i + 0]), bufSize)
  191. inc(i, bytes)
  192. setLen(result, i)