summary refs log tree commit diff stats
path: root/compiler
diff options
context:
space:
mode:
authorZahary Karadjov <zahary@gmail.com>2012-11-21 17:49:50 +0200
committerZahary Karadjov <zahary@gmail.com>2012-11-28 01:15:13 +0200
commitf644e3079f451c1ee71376ada120a789c696423b (patch)
treef9bf8071693127ebe4bfb31eb4bca93140234618 /compiler
parente6f3f46cd94325253f9ade2e8fb39a494fce5072 (diff)
downloadNim-f644e3079f451c1ee71376ada120a789c696423b.tar.gz
experimental compile-time rope formatting code
Diffstat (limited to 'compiler')
-rwxr-xr-xcompiler/cgen.nim76
-rwxr-xr-xcompiler/main.nim5
-rwxr-xr-xcompiler/ropes.nim25
3 files changed, 102 insertions, 4 deletions
diff --git a/compiler/cgen.nim b/compiler/cgen.nim
index 3a8df7bd8..0a903f95b 100755
--- a/compiler/cgen.nim
+++ b/compiler/cgen.nim
@@ -106,10 +106,10 @@ proc ropecg(m: BModule, frmt: TFormatStr, args: varargs[PRope]): PRope =
           internalError("ropes: invalid format string $" & $j)
         app(result, args[j-1])
       of 'n':
-        if optLineDir notin gOptions: app(result, tnl)
+        if optLineDir notin gOptions: app(result, rnl)
         inc(i)
       of 'N': 
-        app(result, tnl)
+        app(result, rnl)
         inc(i)
       else: InternalError("ropes: invalid format string $" & frmt[i])
     elif frmt[i] == '#' and frmt[i+1] in IdentStartChars:
@@ -133,6 +133,74 @@ proc ropecg(m: BModule, frmt: TFormatStr, args: varargs[PRope]): PRope =
     if i - 1 >= start: 
       app(result, substr(frmt, start, i - 1))
 
+import macros
+
+type TFmtFragmentKind = enum
+  ffSym,
+  ffLit,
+  ffParam
+
+type TFragment = object
+  case kind: TFmtFragmentKind
+  of ffSym, ffLit:
+    value: string
+  of ffParam:
+    intValue: int
+
+iterator fmtStringFragments(s: string): tuple[kind: TFmtFragmentKind,
+                                              value: string,
+                                              intValue: int] =
+  # This is a bit less featured version of the ropecg's algorithm
+  # (be careful when replacing ropecg calls)
+  var
+    i = 0
+    length = s.len
+
+  while i < length:
+    var start = i
+    case s[i]
+    of '$':
+      let n = s[i+1]
+      case n
+      of '$':
+        inc i, 2
+      of '0'..'9':
+        # XXX: use the new case object construction syntax when it's ready
+        yield (kind: ffParam, value: "", intValue: n.ord - ord('1'))
+        inc i, 2
+        start = i
+      else:
+        inc i
+    of '#':
+      inc i
+      var j = i
+      while s[i] in IdentChars: inc i
+      yield (kind: ffSym, value: substr(s, j, i-1), intValue: 0)
+      start = i
+    else: nil
+
+    while i < length:
+      if s[i] != '$' and s[i] != '#': inc i
+      else: break
+
+    if i - 1 >= start:
+      yield (kind: ffLit, value: substr(s, start, i-1), intValue: 0)
+
+macro rfmt(m: BModule, fmt: expr[string], args: varargs[PRope]): expr =
+  ## Experimental optimized rope-formatting operator
+  ## The run-time code it produces will be very fast, but will it speed up
+  ## the compilation of nimrod itself or will the macro execution time
+  ## offset the gains?
+  result = newCall(bindSym"ropeConcat")
+  for frag in fmtStringFragments(fmt.strVal):
+    case frag.kind
+    of ffSym:
+      result.add(newCall(bindSym"cgsym", m, newStrLitNode(frag.value)))
+    of ffLit:
+      result.add(newCall(bindSym"~", newStrLitNode(frag.value)))
+    of ffParam:
+      result.add(args[frag.intValue])
+  
 proc appcg(m: BModule, c: var PRope, frmt: TFormatStr, 
            args: varargs[PRope]) = 
   app(c, ropecg(m, frmt, args))
@@ -164,6 +232,10 @@ proc lineCg(p: BProc, s: TCProcSection, frmt: TFormatStr,
                args: varargs[PRope]) =
   app(p.s(s), indentLine(p, ropecg(p.module, frmt, args)))
 
+template lineCg2(p: BProc, s: TCProcSection, frmt: TFormatStr,
+                 args: varargs[PRope]) =
+  line(p, s, rfmt(p.module, frmt, args))
+
 proc appLineCg(p: BProc, r: var PRope, frmt: TFormatStr,
                args: varargs[PRope]) =
   app(r, indentLine(p, ropecg(p.module, frmt, args)))
diff --git a/compiler/main.nim b/compiler/main.nim
index 15fa8b4fb..96ebfc5ef 100755
--- a/compiler/main.nim
+++ b/compiler/main.nim
@@ -501,3 +501,8 @@ proc MainCommand =
                formatFloat(epochTime() - gLastCmdTime, ffDecimal, 3),
                formatSize(getTotalMem())])
 
+  echo "rope cache stats: "
+  echo "  tries : ", gCacheTries
+  echo "  misses: ", gCacheMisses
+  echo "  efficiency: ", formatFloat(1-(gCacheMisses.float/gCacheTries.float), ffDecimal, 3)
+
diff --git a/compiler/ropes.nim b/compiler/ropes.nim
index 50c89e4d9..a539418ca 100755
--- a/compiler/ropes.nim
+++ b/compiler/ropes.nim
@@ -127,10 +127,15 @@ proc RopeInvariant(r: PRope): bool =
                   #      if result then result := ropeInvariant(r.right);
                   #    end 
 
+var gCacheTries* = 0
+var gCacheMisses* = 0
+
 proc insertInCache(s: string): PRope = 
+  inc gCacheTries
   var h = hash(s) and high(cache)
   result = cache[h]
   if isNil(result) or result.data != s:
+    inc gCacheMisses
     result = newRope(s)
     cache[h] = result
   
@@ -186,6 +191,10 @@ proc con(a: string, b: PRope): PRope = result = con(toRope(a), b)
 proc con(a: varargs[PRope]): PRope = 
   for i in countup(0, high(a)): result = con(result, a[i])
 
+proc ropeConcat*(a: varargs[PRope]): PRope =
+  # not overloaded version of concat to speed-up `rfmt` a little bit
+  for i in countup(0, high(a)): result = con(result, a[i])
+
 proc toRope(i: BiggestInt): PRope = result = toRope($i)
 
 proc app(a: var PRope, b: PRope) = a = con(a, b)
@@ -212,6 +221,10 @@ proc WriteRope*(head: PRope, filename: string, useWarning = false) =
     rawMessage(if useWarning: warnCannotOpenFile else: errCannotOpenFile,
                filename)
 
+var
+  rnl* = tnl.newRope
+  softRnl* = tnl.newRope
+
 proc ropef(frmt: TFormatStr, args: varargs[PRope]): PRope = 
   var i = 0
   var length = len(frmt)
@@ -240,10 +253,10 @@ proc ropef(frmt: TFormatStr, args: varargs[PRope]): PRope =
         else:
           app(result, args[j - 1])
       of 'n':
-        if optLineDir notin gOptions: app(result, tnl)
+        app(result, softRnl)
         inc i
       of 'N':
-        app(result, tnl)
+        app(result, rnl)
         inc(i)
       else: InternalError("ropes: invalid format string $" & frmt[i])
     var start = i
@@ -254,6 +267,14 @@ proc ropef(frmt: TFormatStr, args: varargs[PRope]): PRope =
       app(result, substr(frmt, start, i - 1))
   assert(RopeInvariant(result))
 
+{.push stack_trace: off, line_trace: off.}
+proc `~`*(r: expr[string]): PRope =
+  # this is the new optimized "to rope" operator
+  # the mnemonic is that `~` looks a bit like a rope :)
+  var r {.global.} = r.ropef
+  return r
+{.pop.}
+
 proc appf(c: var PRope, frmt: TFormatStr, args: varargs[PRope]) = 
   app(c, ropef(frmt, args))