llstream.nim 5.8 KB

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