llstream.nim 5.8 KB

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