# # # The Nim Compiler # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # # Ropes for the C code generator. Ropes are mapped to `string` directly nowadays. from pathutils import AbsoluteFile when defined(nimPreviewSlimSystem): import std/[assertions, syncio, formatfloat] type FormatStr* = string # later we may change it to CString for better # performance of the code generator (assignments # copy the format strings # though it is not necessary) Rope* = string proc newRopeAppender*(cap = 80): string {.inline.} = result = newStringOfCap(cap) proc freeze*(r: Rope) {.inline.} = discard proc resetRopeCache* = discard template rope*(s: string): string = s proc rope*(i: BiggestInt): Rope = ## Converts an int to a rope. result = rope($i) proc rope*(f: BiggestFloat): Rope = ## Converts a float to a rope. result = rope($f) proc writeRope*(f: File, r: Rope) = ## writes a rope to a file. write(f, r) proc writeRope*(head: Rope, filename: AbsoluteFile): bool = var f: File = default(File) if open(f, filename.string, fmWrite): writeRope(f, head) close(f) result = true else: result = false proc prepend*(a: var Rope, b: string) = a = b & a proc runtimeFormat*(frmt: FormatStr, args: openArray[Rope]): Rope = var i = 0 result = newRopeAppender() var num = 0 while i < frmt.len: if frmt[i] == '$': inc(i) # skip '$' case frmt[i] of '$': result.add("$") inc(i) of '#': inc(i) result.add(args[num]) inc(num) of '0'..'9': var j = 0 while true: j = j * 10 + ord(frmt[i]) - ord('0') inc(i) if i >= frmt.len or frmt[i] notin {'0'..'9'}: break num = j if j > high(args) + 1: raiseAssert "invalid format string: " & frmt else: result.add(args[j-1]) of '{': inc(i) var j = 0 while frmt[i] in {'0'..'9'}: j = j * 10 + ord(frmt[i]) - ord('0') inc(i) num = j if frmt[i] == '}': inc(i) else: raiseAssert "invalid format string: " & frmt if j > high(args) + 1: raiseAssert "invalid format string: " & frmt else: result.add(args[j-1]) of 'n': result.add("\n") inc(i) of 'N': result.add("\n") inc(i) else: raiseAssert "invalid format string: " & frmt else: result.add(frmt[i]) inc(i) proc `%`*(frmt: static[FormatStr], args: openArray[Rope]): Rope = runtimeFormat(frmt, args) template addf*(c: var Rope, frmt: FormatStr, args: openArray[Rope]) = ## shortcut for ``add(c, frmt % args)``. c.add(frmt % args) const bufSize = 1024 # 1 KB is reasonable proc equalsFile*(s: Rope, f: File): bool = ## returns true if the contents of the file `f` equal `r`. var buf: array[bufSize, char] = default(array[bufSize, char]) bpos = buf.len blen = buf.len btotal = 0 rtotal = 0 when true: var spos = 0 rtotal += s.len while spos < s.len: if bpos == blen: # Read more data bpos = 0 blen = readBuffer(f, addr(buf[0]), buf.len) btotal += blen if blen == 0: # no more data in file result = false return let n = min(blen - bpos, s.len - spos) # TODO There's gotta be a better way of comparing here... if not equalMem(addr(buf[bpos]), cast[pointer](cast[int](cstring(s))+spos), n): result = false return spos += n bpos += n result = readBuffer(f, addr(buf[0]), 1) == 0 and btotal == rtotal # check that we've read all proc equalsFile*(r: Rope, filename: AbsoluteFile): bool = ## returns true if the contents of the file `f` equal `r`. If `f` does not ## exist, false is returned. var f: File = default(File) result = open(f, filename.string) if result: result = equalsFile(r, f) close(f)