# # # The Nimrod Compiler # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # ## Low-level streams for high performance. import strutils when not defined(windows) and defined(useGnuReadline): import rdstdin type TLLStreamKind* = enum # enum of different stream implementations llsNone, # null stream: reading and writing has no effect llsString, # stream encapsulates a string llsFile, # stream encapsulates a file llsStdIn # stream encapsulates stdin TLLStream* = object of TObject kind*: TLLStreamKind # accessible for low-level access (lexbase uses this) f*: tfile s*: string rd*, wr*: int # for string streams lineOffset*: int # for fake stdin line numbers PLLStream* = ref TLLStream proc LLStreamOpen*(data: string): PLLStream proc LLStreamOpen*(f: var tfile): PLLStream proc LLStreamOpen*(filename: string, mode: TFileMode): PLLStream proc LLStreamOpen*(): PLLStream proc LLStreamOpenStdIn*(): PLLStream proc LLStreamClose*(s: PLLStream) proc LLStreamRead*(s: PLLStream, buf: pointer, bufLen: int): int proc LLStreamReadLine*(s: PLLStream, line: var string): bool proc LLStreamReadAll*(s: PLLStream): string proc LLStreamWrite*(s: PLLStream, data: string) proc LLStreamWrite*(s: PLLStream, data: Char) proc LLStreamWrite*(s: PLLStream, buf: pointer, buflen: int) proc LLStreamWriteln*(s: PLLStream, data: string) # implementation proc LLStreamOpen(data: string): PLLStream = new(result) result.s = data result.kind = llsString proc LLStreamOpen(f: var tfile): PLLStream = new(result) result.f = f result.kind = llsFile proc LLStreamOpen(filename: string, mode: TFileMode): PLLStream = new(result) result.kind = llsFile if not open(result.f, filename, mode): result = nil proc LLStreamOpen(): PLLStream = new(result) result.kind = llsNone proc LLStreamOpenStdIn(): PLLStream = new(result) result.kind = llsStdIn result.s = "" result.lineOffset = -1 proc LLStreamClose(s: PLLStream) = case s.kind of llsNone, llsString, llsStdIn: nil of llsFile: close(s.f) when not defined(ReadLineFromStdin): # fallback implementation: proc ReadLineFromStdin(prompt: string, line: var string): bool = stdout.write(prompt) result = readLine(stdin, line) proc endsWith*(x: string, s: set[char]): bool = var i = x.len-1 while i >= 0 and x[i] == ' ': dec(i) if i >= 0 and x[i] in s: result = true const LineContinuationOprs = {'+', '-', '*', '/', '\\', '<', '>', '!', '?', '^', '|', '%', '&', '$', '@', '~', ','} AdditionalLineContinuationOprs = {'#', ':', '='} proc endsWithOpr*(x: string): bool = # also used by the standard template filter: result = x.endsWith(LineContinuationOprs) proc continueLine(line: string, inTripleString: bool): bool {.inline.} = result = inTriplestring or line[0] == ' ' or line.endsWith(LineContinuationOprs+AdditionalLineContinuationOprs) proc LLreadFromStdin(s: PLLStream, buf: pointer, bufLen: int): int = var inTripleString = false s.s = "" s.rd = 0 var line = newStringOfCap(120) while ReadLineFromStdin(if s.s.len == 0: ">>> " else: "... ", line): add(s.s, line) add(s.s, "\n") if line.contains("\"\"\""): inTripleString = not inTripleString if not continueLine(line, inTripleString): break inc(s.lineOffset) result = min(bufLen, len(s.s) - s.rd) if result > 0: copyMem(buf, addr(s.s[s.rd]), result) inc(s.rd, result) proc LLStreamRead(s: PLLStream, buf: pointer, bufLen: int): int = case s.kind of llsNone: result = 0 of llsString: result = min(bufLen, len(s.s) - s.rd) if result > 0: copyMem(buf, addr(s.s[0 + s.rd]), result) inc(s.rd, result) of llsFile: result = readBuffer(s.f, buf, bufLen) of llsStdIn: result = LLreadFromStdin(s, buf, bufLen) proc LLStreamReadLine(s: PLLStream, line: var string): bool = setLen(line, 0) case s.kind of llsNone: result = true of llsString: while s.rd < len(s.s): case s.s[s.rd] of '\x0D': inc(s.rd) if s.s[s.rd] == '\x0A': inc(s.rd) break of '\x0A': inc(s.rd) break else: add(line, s.s[s.rd]) inc(s.rd) result = line.len > 0 or s.rd < len(s.s) of llsFile: result = readLine(s.f, line) of llsStdIn: result = readLine(stdin, line) proc LLStreamWrite(s: PLLStream, data: string) = case s.kind of llsNone, llsStdIn: nil of llsString: add(s.s, data) inc(s.wr, len(data)) of llsFile: write(s.f, data) proc LLStreamWriteln(s: PLLStream, data: string) = LLStreamWrite(s, data) LLStreamWrite(s, "\n") proc LLStreamWrite(s: PLLStream, data: Char) = var c: char case s.kind of llsNone, llsStdIn: nil of llsString: add(s.s, data) inc(s.wr) of llsFile: c = data discard writeBuffer(s.f, addr(c), sizeof(c)) proc LLStreamWrite(s: PLLStream, buf: pointer, buflen: int) = case s.kind of llsNone, llsStdIn: nil of llsString: if bufLen > 0: setlen(s.s, len(s.s) + bufLen) copyMem(addr(s.s[0 + s.wr]), buf, bufLen) inc(s.wr, bufLen) of llsFile: discard writeBuffer(s.f, buf, bufLen) proc LLStreamReadAll(s: PLLStream): string = const bufSize = 2048 var bytes, i: int case s.kind of llsNone, llsStdIn: result = "" of llsString: if s.rd == 0: result = s.s else: result = substr(s.s, s.rd) s.rd = len(s.s) of llsFile: result = newString(bufSize) bytes = readBuffer(s.f, addr(result[0]), bufSize) i = bytes while bytes == bufSize: setlen(result, i + bufSize) bytes = readBuffer(s.f, addr(result[i + 0]), bufSize) inc(i, bytes) setlen(result, i)