summary refs log blame commit diff stats
path: root/compiler/llstream.nim
blob: 1d17221765f88dcb239cb9d5a251a53c7f3557b7 (plain) (tree)
1
2
3
4
5
6
7
8
9


                               
                                         




                                                   
                                          



          


                                                      
     
                                                                        


                                                                              
                                                         
                                
                                                                              


                                                  
                                                           









                                                                
                                                            




                                                            
























                                                                  
                        






                                   


                                     
                                                                  
                        
                                  











                                                                            

                                              






                                                                        
                                                                     
                            

          

                                                                       

                  


                                                    
                   

                                       
                                         















                                                                  

                                                             
             





                          
                 


                                         
                 


                            
                 





                                            














































                                                              
                                    









                                                           
#
#
#           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)