llstream.nim 5.4 KB

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